目录
代码例子参考:
https://gitee.com/feistel/Blog/tree/master/%E5%89%8D%E7%AB%AF/forTest/src
webpack工程例子参考:
https://gitee.com/feistel/Blog/tree/master/%E5%89%8D%E7%AB%AF/demo
初识Vue.js
它提供了现代Web开发中的常见高级功能:
- 解耦视图与数据
- 可复用的组件
- 前端路由
- 状态管理
- 虚拟DOM
指令
v-html
插入Html
v-pre
被修饰的元素其内部的字符不会被编译转换
v-bind
> :
动态更新HTML元素或组件元素的属性,如id,class,href,src等
v-bind绑定class的方式:
-
对象语法
<div :class="{'active': isActive}"></div>
当isActive为true时,渲染为
<div class="active"></div>
-
数组语法
<div> :class="[activeCls, errorCls]"</div> date: { activeCls: 'active', errorCls: 'error' }
渲染后为
<div class="active error"></div>
内联样式style与class用法一样。
v-on
> @
监听原生的DOM事件
事件修饰符(可以串联),如
.stop,阻止单击事件冒泡?
@submi.prevent,提交事件不重载页面
.capture
.self
@click.once,只触发一次
@click.native,表示修饰的是一个原生事件
对于@keyCode,包括但不限于:
.enter
.tab
.esc
.up
.down
.left
.right
v-cloak
它会在Vue实例结束编译时从绑定的html元素上移除,常和display:none配合使用 主要解决当vue.js还没加载时,会在页面上显示{{ xxx }}的字样,一闪而过,等Vue创建实例时才会被替换,可以连续点F5查看到。
<style type="text/css">
[v-cloak] {
display: none;
}
</style>
<div id="app" v-cloak>
</div>
v-once
首次渲染后,不再随数据的变化重新渲染,被视为静态内容,到需要优化时用到。 v-if、v-else-if、v-else 条件渲染
v-show
为true时展示。改变元素的css属性display。 与v-if相比,适合于频繁切换条件的场景使用。
v-for
被修饰的元素,将被循环渲染。用法如:
<ul>
<li v-for="book in books">{{ book.name }}</li>
</ul>
v-model
当使用中文输入法,还没进行选词时不会实时绑定数据。可以用@input来实现,如下
<input type="text" @input="handleInput" placeholder="实时输入。。。">
<h1>还没选词也会显示:{{ message }}</h1>
methods: {
handleInput: function (e) {
this.message = e.target.value;
}
}
与事件v-on(@)类似,v-model(:)也有修饰符,用于控制数据同步的时机。
.lazy
<input type="text" v-model.lazy="namee" placeholder="你的名字">
并不是实时改变,而是在失去焦点或回车时才更新
.number
将输入转换为Number类型
.trim
自动过滤输入的首尾空格
自定义指令
全局指令
Vue.directive('focus', {
// 指令选项
})
局部指令
var app = new Vue({
el: '#app',
directives: {
focus: {
// 指令选项
}
}
})
渲染页面是,input将获得焦点。
<input type="text" v-focus>
Vue.directive('focus', {
inserted: function (el) {
el.focus();
}
});
钩子函数
- bind,第一次绑定到元素是调用
- inserted,被绑定元素插入父节点时调用,例如:页面渲染时
- update,被绑定元素所在的末班更新时调用
- componentUpdated,被绑定元素所在模板完成一次更新 周期时调用
- unbind,指令与元素解绑时调用
钩子函数的参数
- el,指令所绑定的元素,可以用来直接操作DOM
- binding,包括如下属性
- name,指令名,不包括v-前缀,如v-focus,name为focus
- value,指令绑定的值,如v-focus="1+1",value为2
- oldValue,指令的前一个值
- expression,绑定值得字符串形式,如v-focus="1+1",expression为"1+1"
- arg,传给指令的参数,如v-focus:foo,arg为foo
- modifiers,一个包含修饰符的对象,如v-focus.foo.bar,modifiers为{foo: true, bar: ture}
- vnode,Vue编译生成的虚拟节点
- oldVnode,上一个虚拟节点
计算属性
computed
只能被当做属性来用,不能加'()',即不能送参数。
computed与methods的区别是:
-
不能接受参数
-
使用时当做属性,而不是方法,不能加'()'
-
计算属性基于依赖缓存,例如, 计算Date.now(),不会刷新,而在methods会。
<div> {{ now }}</div> <div> {{ nowDate() }}</div> // 计算属性 computed: { now: function () { return Date.now(); } }, methods: { nowDate: function () { return Date.now(); } },
表单与v-model
单选按钮
<br>
<input type="radio" v-model="picked" value="html" id="id_html">
<label for="html">HTML</label>
<br>
<input type="radio" v-model="picked" value="js" id="id_js">
<label for="js">javaScript</label>
<p>选择的项是: {{ picked }} </p>
picked: "js"
复选框
-
单个使用时,用v-model来绑定一个布尔值
-
组合使用时,绑定到同一个数组类型
<br> <input type="checkbox" v-model="checked" value="html" id="id_html"> <label for="html">HTML</label> <br> <input type="checkbox" v-model="checked" value="js" id="id_js"> <label for="js">javaScript</label> <p>选择的项是: {{ checked }} </p> checked: []
选择列表(下拉框)
<select v-model="selected" multiple>
<option></option>
<option value="js">javaScript</option>
<option value="css">css</option>
</select>
<p>选择的项是: {{ selected }} </p>
selected: []
组件
全局组件
Vue.component('my-component', {
template: '<div>组件内容</div>'
})
局部组件
var Child = {
template: '<div>组件内容</div>'
}
var app = new Vue({
el: '#app',
component: {
'my-component': Child
}
})
组件数据与方法
在使用data时,和Vue实例有区别,data必须是函数,然后将数据return出去。
Vue.component('my-component', {
template: '<div>{{ message }}</div>',
data: function () {
return {
message: '组件内容'
}
}
});
使用props传递数据
父组件中包含子组件,父组件哟啊正向传递数据或参数,这个正向传递过程就是通过props实现的。
> Vue 2.x与Vue 1.x的改变就是props传递数据是单向的了,只能正向传递给子组件。尽可能将父子组件解耦。 > > Vue 1.x提供了.sync
修饰符来支持双向绑定。
组件中,props选项声明需要从父级接受的数据,props可以是:
-
数组
<!-- html不区分大小写,这里必须要用中横杆分隔命名 --> <!-- 传递进来的通常不是写死的,而是来自父级的动态数据,使用v-bind来动态绑定。 --> <input type="text" v-model="parentMessage"> <my-component :props-message="parenMessage"></my-component> Vue.component('my-component', { // 必须使用驼峰命名 props: ['propsMessage'], template: '<div>{{ propsMessage }}--{{ message }}</div>', data: function () { return { message: '组件内容' } } });
-
对象
当prop需要验证时,就需要对象写法。
props: { propsMessage: { type: Boolean, default: true } }
type类型可以是:
String
Number
Boolean
Object
Array
Function
组件通信
自定义事件
在组件里使用this.$emit('increase', this.counter);
触发事件
在使用组件处使用<my-component @increase="handleIncrease"></my-component>
接受事件并在handleIncrease
处理。
其中,this.counter
表示从组件里传出的数据,将作为handleIncrease
的入参,实现形如:
function handleIncrease (counter) {}
> 语法糖: > > 在组件里使用this.$emit('input', this.counter);
触发事件 > > 在使用组件处使用<my-component v-model="currentCounter"></my-component>
> > 即可到达将子类数据,反向绑定到父组件的currentCounter中。
非父子组件通信
Vue.js 1.x
提供了$.dispatch()和$broadcast(),在Vue.js 2.x被废弃
组件内使用this.$dispatch('on-message', '内部组件的数据');
发送事件
Vue.js 2.x
1.使用中央事件总线(bus)
> 当项目比较大,可以选择更好的状态管理解决方案vuex
var bus = new Vue();
Vue.component('my-component', {
template: '<button @click="handleEvent">传递事件</button>',
methods: {
handleEvent: function () {
bus.$emit('on-message', '来自my-component组件的数据');
}
}
});
var app = new Vue({
el: '#app',
data: {
message: ''
},
mounted: function () {
var _this = this;
bus.$on('on-message', function (msg) {
_this.message = msg;
});
}
});
2.父链
在子组件里直接使用this.$parent.message
,message表示父级以上的变量。
或者在父组件使用,this.$children.message
> 父链使得父子组件紧耦合,最好还是通过props和$emit来通信。
3.子组件索引
使用组件时加上ref
属性
<my-component ref="comA"></my-component>
使用this.$refs.comA.message
获取组件内部数据,message表示组件的内部变量。
使用slot分发内容
> slot分发的内容,作用域时在父组件上的。 > > 人话:slot分发的内容中,内容是指,组件里的内容,比如,<my-component><p>内容</p></my-component>,其中'<p>内容</p>'就表示要分发的内容;分发是指内容将会被插入到子组件里。而编译的这个内容的作用域是父组件的,而不属于子组件。
其他
$nextTick
methods: {
getText: function () {
this.$nextTick (function () {
var text = document.getElementById('div').innerHTML;
console.log(text);
});
}
}
> 由于Vue异步更新DOM原理。Vue在观察到数据变化是并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中,在下一个事件循环tick中,Vue刷新队列并执行实际的工作。
$nextTick表示在下一个事件循环中执行。
手动挂载组件
new MyComponent().$mount('#mount-div');
使用Webpack 2
> 单页富应用(SPA),就是只有一张Web页面的应用。单页应用程序 (SPA) 是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。 > > 百度百科https://baike.baidu.com/item/SPA/17536313?fr=aladdin
Vue工程配置
新建目录demo目录
npm init将生成一个package.json文件
npm init
局部安装webpack
npm install webpack --save-dev
使用命令 --save 或者说不写命令 --save ,都会把信息记录到 dependencies 中
dependencies 中记录的都是项目在运行时需要的文件
使用命令 --save-dev 则会把信息记录到devDependencies 中
devDependencies 中记录的是项目在开发过程中需要使用的一些文件,而在项目最终发布时是不需要的
> --save 和 --save-dev 的作用和区别简单描述: > > https://blog.youkuaiyun.com/cvper/article/details/88728505
局部安装webpack-dev-server,它提供很多服务,如启动一个服务器,热更新,接口代理。
npm install webpack-dev-server --save-dev
安装
npm install webpack-cli
package.json文件内容:
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.43.0",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"webpack-cli": "^3.3.11"
}
}
webpack就是一个.js配置文件。
新建空的main.js文件,作为入口文件。
新建文件webpack.config.js,如下
var path = require('path');
var config = {
entry: {
main: './main'
},
output: {
// 指定打包后文件的输出目录
path: path.join(__dirname, './dist'),
// 指定资源文件引用的目录
publicPath: '/dist/',
// 指定输出文件的名称
filename: 'main.js'
}
};
module.exports = config;
新建index.html作为SPA的入口
<meta charset="utf-8">
<title>webpack App</title>
<div id="app">
Hello World.
</div>
<script type="text/javascript" src="/dist/main.js"></script>
配置package.json,当运行npm run dev时,将执行该命令。默认地址是127.0.0.1:8080。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --config webpack.config.js"
},
使用命令启动
npm run dev
在main.js文件中,增加,保存,浏览器会实时显示改变内容,这就是webpack-dev-server的热更新功能。
document.getElementById('app').innerHTML = 'Hello webpack';
完善工程配置
在webpack里不同的模块需要不同的加载器(Loaders)来处理。
npm install css-loader --save-dev
npm install style-loader --save-dev
具体参考demo文件夹:
https://gitee.com/feistel/Blog/tree/master/%E5%89%8D%E7%AB%AF/demo
Render函数
用法参考:
JSX
简化render函数createElement盖楼一样的嵌套,Vue.js提供了插件babel-plugin-transform-vue-jsx来支持JSX语法,需要在webpack中配置编译器。
插件
前端路由
SPA的核心就是前端路由
SPA只有一个html,整个网站的内容都在这个html里,通过JavaScript来处理。
前端路由维护一个路由规则,使得url看起来像普通网站那样,以'/'分隔。
vue-router实现原理,路由不同的页面事实上就是动态加载不同的组件。
路由工程例子参考:
https://gitee.com/feistel/Blog/tree/master/%E5%89%8D%E7%AB%AF/demo
状态管理与Vuex
Vuex,管理和维护整个项目组件的状态。
与bus(中央事件总线)类似,用户触发和接收事件,进一步起到通讯的作用。
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0,
list: [1, 5, 8, 10, 30, 50]
},
getters: {
filteredList: state => {
return state.list.filter(item => item < 10);
},
listCount: (state, getters) => {
return getters.filteredList.length;
}
},
mutations: {
increment (state, n = 1) {
state.count += n;
},
decrease (state) {
state.count --;
}
},
actions: {
increment (context) {
context.commit('increment');
},
asyncIncrement (context) {
return new Promise(resolve => {
setTimeout(() => {
context.commit('increment');
resolve();
}, 1000)
});
}
}
});
new Vue({
el: '#app',
router: router,
store: store,
render: h => {
return h(App)
}
});
使用:
<script>
export default {
computed: {
count () {
return this.$store.state.count;
},
list () {
return this.$store.getters.filteredList;
},
listCount () {
return this.$store.getters.listCount;
}
},
methods: {
handleIncrement () {
this.$store.commit('increment');
},
handleDecrease () {
this.$store.commit('decrease');
},
handleIncrementMore () {
this.$store.commit('increment', 5);
},
handleActionIncrement () {
this.$store.dispatch('increment')
},
handleAsyncIncrement () {
this.$store.dispatch('asyncIncrement').then(() => {
console.log(this.$store.state.count);
});
}
}
}
</script>
《Vue.js实战》 梁灏