我们使用webpack也可以手动安装配置vue脚手架环境,但是有一套更简单一点的vue-cli的构建工具,大大降低了webpack的使用难度。
npm install -g vue-cli
完成之后查看vue版本:
vue -V
如果终端承认vue命令,那么脚手架安装成功,否则要么脚手架安装失败,要么就是需要配置vue的path变量。
1. 查看所有vue命令
vue -h
运行结果:
Options:
-V, --version output the version number
-h, --help output usage information
Commands:
init generate a new project from a template
list list available official templates
build prototype a new project
create (for v3 warning only)
help [cmd] display help for [cmd]
2. 构建vue项目
vue init webpack my-pro
在安装过程中,会出现以下一些选择:
? Project name 输入项目名称
? Project description 输入项目描述
? Author 作者
? Vue build 打包方式,回车就好了
? Install vue-router? 选择 Y 使用 vue-router,输入 N 不使用
? Use ESLint to lint your code? 代码规范,推荐 N
? Setup unit tests with Karma + Mocha? 单元测试,推荐 N
? Setup e2e tests with Nightwatch? E2E测试,N
3. 项目的目录结构
├── README.md // 项目说明文档
├── node_modules // 依赖包目录
├── build // webpack相关配置文件(都已配置好,一般无需再配置)
│ ├── build.js //生成环境构建
│ ├── check-versions.js //版本检查(node,npm)
│ ├── dev-client.js //开发服务器热重载 (实现页面的自动刷新)
│ ├── dev-server.js //构建本地服务器(npm run dev)
│ ├── utils.js // 构建相关工具
│ ├── vue-loader.conf.js //csss 加载器配置
│ ├── webpack.base.conf.js //webpack基础配置
│ ├── webpack.dev.conf.js // webpack开发环境配置
│ └── webpack.prod.conf.js //webpack生产环境配置
├── config // vue基本配置文件(可配置监听端口,打包输出等)
│ ├── dev.env.js // 项目开发环境配置
│ ├── index.js // 项目主要配置(包括监听端口、打包路径等)
│ └── prod.env.js // 生产环境配置
├── index.html // 项目入口文件
├── package-lock.json // npm5 新增文件,优化性能
├── package.json // 项目依赖包配置文件
├── src // 项目核心文件(存放我们编写的源码文件)
│ ├── App.vue // 根组件
│ ├── assets // 静态资源(样式类文件、如css,less,和一些外部的js文件)
│ │ └── css //样式
│ │ └── font //字体
│ │ └── images //图片
│ ├── components // 组件目录
│ │ └── Hello.vue // 测试组件
│ ├── main.js // 入口js文件
│ └── router // 路由配置文件夹
│ └── index.js // 路由配置文件
└── static // 静态资源目录(一般存放图片类)
注:assets和static文件夹的区别
assets目录中的文件会被webpack处理解析为模块依赖,只支持相对路径形式。例如,在
和 background: url(./logo.png)中
,"./logo.png"
是相对的资源路径,将由Webpack解析为模块依赖。
static/
目录下的文件并不会被Webpack处理:它们会直接被复制到最终的打包目录(默认是dist/static
)下。必须使用绝对路径引用这些文件,这是通过在 config.js
文件中的 build.assetsPublicPath
和 build.assetsSubDirectory
连接来确定的。
任何放在 static/
中文件需要以绝对路径的形式引用:/static/[filename]
。
在我们实际的开发中,总的来说:static放不会变动的文件 assets放可能会变动的文件。
4. 安装好了之后启动项目
npm run dev
vue-cli开发的是单页面的应用,只有一个 html 文件,在开发时,我们将 整个项目按功能划分成模块,每个模块都可以被任意的复用。高效率,低耦合。
模块的具体呈现形式就是组件,一个模块就是一个组件,每个组件有自己的数据、函数、周期等。
项目默认安装好了之后,可以在 src 文件夹中看到有一个默认的HelloWorld.vue文件。这就是默认安装的组件,一个组件就是一个 vue 文件。
我们在 src 的 components 文件夹中创建组件。
<template>
<div class="home">
div>
template>
<script>
// 当前组件的脚本
export default {
// 当前组件的名称
name: 'Home',
data () {
return {
// 当前组件中所有的数据
}
},
methods: {
// 当前组件中所有的函数
}
}
script>
<style>
/* 当前组件的样式 */
style>
注:
标签中。
标签中的样式,是全局的样式,如果这个组件被其他组件调用,那么这个组件中的style样式会作用于当前页面中的所用同选择器元素。
标签加了scoped(如
)属性之后,其样式就是局部的,只作用于当前组件。组件的调用方式有多种,作为子组件被调用,路由调用,插槽调用等,在这里我们先说传统的调用方式,即作为子组件被调用。
调用组建时,需要这么几个步骤:
exprot default {}
。
<template>
<div id="app">
<Home/>
div>
template>
<script>
// 导入Home.vue
import Home from './components/home'
export default {
name: 'App',
components: {
// 在此声明
Home
}
}
script>
<style>style>
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等,我们把这一系列的过程称为组件的生命周期(组件从注册到销毁的整个过程)。我们有时候需要在组件生命周期的某个过程中,执行某些代码,基于此,vue 提供了生命周期钩子函数,给了用户在不同阶段添加自己的代码的机会。
但是在此之前,我们要详细的介绍下组件的声明周期,以及生命周期中每个阶段组件完成和未完成的部分。
从图中可以看到,vue 为生命周期提供了 8 个钩子函数:
我们可以在组件的生命周期钩子函数中执行一些代码。
<template>
<div id="app">
<button @click="flag = !flag">点击button>
<Home v-if="flag"/>
div>
template>
<script>
import Home from './components/home'
export default {
name: 'App',
components: {
Home
},
data() {
return {
flag: true,
info: 'i am info of component'
}
},
//数据没有 元素没有
beforeCreate:function(){
console.log("创建前========");
console.log(this.info);
console.log(this.$el);
},
//数据有 元素没有
created:function(){
console.log("已创建========");
console.log(this.info);
console.log(this.$el);
},
//数据有 元素有 虚拟dom
beforeMount:function(){
console.log("mount之前========");
console.log(this.info);
console.log(this.$el);
},
//数据有 元素有,真实dom
mounted:function(){
console.log("mounted========");
console.log(this.info);
console.log(this.$el);
},
beforeUpdate:function(){
console.log("更新前========");
console.log(this.flag);
console.log(this.$el);
},
updated:function(){
console.log("更新完成========");
console.log(this.flag);
console.log(this.$el);
},
beforeDestroy:function(){
console.log("销毁前========");
console.log(this.info);
console.log(this.$el);
},
destroyed:function(){
console.log("已销毁========");
console.log(this.info);
console.log(this.$el);
}
}
script>
<style>style>
注:
computed(计算数据) 和 watch(观察者) 是 vue 组件中非常重要的两个功能。
创建一个组建时,组件中的数据都声明在 data 中,但是有些数据需要依赖 data 中的其他数据计算,这个数据会随着依赖数据的变化而变化(比如购物车中被选中的商品总价,需要通过购物车中的商品列表计算,当购物车中商品发生变化,这个总价也会变化)。
我们把这种需要依赖其他变量进行处理之后产生的数据都声明在computed(计算属性)中。 计算属性允许我们对指定的视图,复杂的值计算。这些值将绑定到依赖项值,只在需要时更新。
计算属性示例:
<template>
<div class="home">
count: <input type="number" v-model.number="current.count">
price: <input type="number" v-model.number="current.price">
<button @click="addGood">添加button>
<h3>{{ sum }}h3>
div>
template>
<script>
export default {
name: "Home",
data() {
return {
current: {
count: 0,
price: 0
},
list: [{
count: 3,
price: 39.79
},{
count: 1,
price: 109.34
},{
count: 3,
price: 0.45
}]
}
},
methods: {
addGood() {
this.list.push(this.current)
}
},
computed: {
sum() {
let sum_ = 0;
for(let l of this.list) {
sum_ += l.count * l.price;
}
return sum_;
}
}
}
script>
注:
computed和methods有时候从代码角度而言,没有区别,但是底层的机制是完全不同的。
methods中的函数,在每次使用这个数据时都要调用,computed依赖于data中的数据,只有在它的相关依赖数据发生改变时才会重新求值,如果依赖数据不变,计算属性会缓存这个数据上次的值,不会重复调用复杂的计算过程。
官方文档反复强调对于任何复杂逻辑,都应当使用computed计算属性
computed和methods的区别示例:
<template>
<div class="home">
<button @click="n++">a变化button>
<button @click="m++">b变化button>
<h3>{{ sum_of_c }}h3>
<h3>{{ sum_of_c }}h3>
<h3>{{ sum_of_c }}h3>
<hr>
<h3>{{ sum_of_m() }}h3>
<h3>{{ sum_of_m() }}h3>
<h3>{{ sum_of_m() }}h3>
div>
template>
<script>
export default {
name: "Home",
data() {
return {
n: 0,
m: 0
}
},
methods: {
sum_of_m() {
console.log('methods中的sum执行了');
return this.n + this.m;
}
},
computed: {
sum_of_c() {
console.log('computed中的sum执行了');
return this.n + this.m;
}
}
}
script>
有时候我们需要在 data 中的数据发生变化时做一些操作,那就需要监听这个数据的变化,vue 提供了 watch(侦听器),来监听数据的变化。
computed 计算依赖于某些数据的值。watch 监听数据本身。
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
方式一:
<template>
<div class="test2">
<button @click="num = 1000">点击button>
div>
template>
<script>
export default {
name: "Test2",
data() {
return {
num: 100
}
},
watch: {
num(newVal, oldVal) {
console.log(newVal, oldVal);
}
}
}
script>
这种写法也可以改写成这样:
// ...
methods: {
numChange(newVal, oldVal) {
console.log(newVal, oldVal);
}
},
watch: {
num: 'numChange'
}
// ...
方式二:
方式一使用 watch 时有一个特点,就是当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。
// ...
watch: {
num: {
handler(newVal, oldVal) {
console.log(newVal, oldVal)
},
// immediate表示在watch中首次绑定的时候,是否执行handler,值为true则表示在watch中声明的时候,就立即执行handler方法,值为false,则和一般使用watch一样,在数据发生变化的时候才执行handler。
immediate: true
}
}
// ...
watch监听对象时,对象的属性和属性的属性发生变化时,默认不会触发对于这个对象的监听。想要在对象的任意属性(无论嵌套多复杂)改变时,执行对象的watch函数,需要开启deep。
watch: {
obj: {
handler(newVal, oldVal) {
// code
},
deep: true
}
}
// ...
只是想监听对象的某个属性的变化:
watch: {
'obj.key': {
handler(newVal, oldVal) {
// code
}
}
}
// ...
除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,Vue提供了一种更简单的方式-自定义指令,方便我们对DOM的操作。
自定义指令声明之后的调用方式和使用方式和 Vue 提供的默认指令一样。
全局的自定义指令,在 main.js 中创建
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时
inserted: function (el) {
// 聚焦元素
el.focus();
}
});
局部的自定义指令,创建在组件中:
export default {
name: 'Home',
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
}
无论是局部的还是全局的,调用方式都相同,只是全局的指令可以被任意组件调用,局部的指令只能被当前组件调用:
<input v-focus type="text">
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
指令钩子函数会被传入以下参数:
更加具体的示例:
Vue.directive('demo', {
bind: function (el, binding, vnode) {
var s = JSON.stringify
el.innerHTML =
'name: ' + s(binding.name) + '
' +
'value: ' + s(binding.value) + '
' +
'expression: ' + s(binding.expression) + '
' +
'argument: ' + s(binding.arg) + '
' +
'modifiers: ' + s(binding.modifiers) + '
' +
'vnode keys: ' + Object.keys(vnode).join(', ');
}
})
调用:
<template>
<div class="test3" v-demo:foo.a.b="message">div>
template>
<script>
export default {
name: "Test3",
data() {
return {
message: 'hello!'
}
}
}
script>
注:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value;
})
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
<div v-demo="{ color: 'white', text: 'hello!' }">div>
练习: 封装v-on指令
参考文档
Vue中的computed
Vue中computed和methods中的区别
Vue中的watch详解