热重载:(hot-reloading)你修改页面之后,自动刷新,不用手动刷新
vue-cli的配置参考 (待仔细学习)
学习传送门:https://cli.vuejs.org/zh/config/#%E5%85%A8%E5%B1%80-cli-%E9%85%8D%E7%BD%AE
webpack配置(待仔细学习)
学习传送门:https://webpack.js.org/configuration/
过渡&动画 (要仔细学习,又是一个门)
学习传送门:https://cn.vuejs.org/v2/guide/transitions.html
通过插槽分发内容、动态组件、解析Dom模板时的注意事项
用key管理可复用的元素
切换input的时候,input就是独立的了
v-if vs v-show
传送门:https://www.jianshu.com/p/2feb8cad6abf
对象变更检测注意事项
var vm=new Vue({
data:{
userProfile:{
name:'anika'
}
}
})
//如果想要给userProfile添加一个属性age,操作如下
Vue.set(vm.userProfile,'age',27)
或者
vm.$set(vm.userProfile,'age',27)
如果想要给已知的对象赋值多个属性可以这样操作
Object.assign({},vm.userProfile,{
age:27,
favoriteColor:'vue Green'
})
计算属性的应用
<li v-for="n in evenNumber"> {{n}}</li>
<script>
new vue({
data():{
numbers:[1,2,3,4],
computed:{
evenNumber:function(){
return this.numbers.filter(function(number){
return number%2===0
})
}
}
}
})
</script>
或者
<li v-for="n in even(numbers)">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
v-for在组件中的应用
<ul>
<li
is="todo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-on:remove="todos.splice(index, 1)"
></li>
</ul>
因为li时ul内部的有效元素,所以通过is="todo-item"的方式在ul中去循环
这样循环,组件内部中需要通过prop去接收item传递的值:例如:
Vue.component('todo-item', {
template: '\
<li>\
{{ title }}\
<button v-on:click="$emit(\'remove\')">Remove</button>\
</li>\
',
props: ['title']
})
事件修饰符
传送门:https://cn.vuejs.org/v2/guide/events.html
.stop阻止单击事件继续传播 可以阻止冒泡行为
<div v-on:click="doThis1" class="btn">外部元素
<div class="s-btn" v-on:click.stop="doThis">
内部元素
</div>
</div>
<script>
export default{
name:'test',
methods:{
doThis(){
console.log('我是内部元素')
},
doThis1(){
console.log('我是外部元素')
}
}
}
</script>
.capture修饰符事件捕获模式
如果有此修饰符会先再此先处理,然后内部的监听事件才会进行处理
例如:
<div v-on:click.capture="doThis2" class="btn3">
1366777777777777777
<div class="btn2" v-on:click="doThis3">
134
</div>
</div>
doThis2(){
console.log('我是1')
},
doThis3(){
console.log('我是2')
}
输出结果为:由外向内
反之:如果没有.capture修饰符输出结果为:由内向外
.self只有当点击的是当前本身的时候才会触发click事件
例如:
代码如下: other在self内部,当点击self的时候,输出结果为
<div v-on:click.capture="doThis2" class="btn3">
1366777777777777777
<div class="btn2" v-on:click="doThis3">
134
<div v-on:click.self="doThat">
self
<div v-on:click="doThat1">
other
</div>
</div>
</div>
</div>
没有self
注意:使用顺序,
例如:v-on:click.prevent.self 会阻止所有的点击,
v-on:click.self.prevent会阻止对元素自身的点击
.passive修饰符尤其能够提供移动端的性能
<div class="click" v-on:scroll.passive="onScroll" >
</div>
不会等待scroll监听器执行完再去执行Onscroll,他会立即执行,因为.passive永远不会调用阻止默认行为的方法event.preventDefault()
表单输入绑定
注意
1.input中的v-mobel这样绑定,不会在输入法组合文字的过程中更新例子如下:
如果想要即时更新的,需要使用@input去实现
<input v-model="message" placeholder="edit me">
2.在textarea文本域中
(<textarea>{{text}}</textarea>) //并不会生效
//应该使用v-mobel替换
<textarea v-model="message" placeholder="add multiple lines"></textarea>
3.select选择框
如果v-mobel表达式的初始值未能匹配任何选项,<select>元素将被渲染为"未选中"状态。
在ios中,这会使用户无法选择第一个选项。因为这样的情况下,ios不会触发change事件。因此,更推荐像上面这样提供一个值为空的禁用选项
4.修饰符 .lazy
<input v-model.lazy="msg" >只有在input失去焦点或者按回车的时候才会被赋值
插槽的使用
动态组件&异步组件
动态组件的应用:
- 多标签的界面中
- 可以运用keep-live标签来保持原来的状态
- 实际应用中的商品详情页面,可能会有多种状态,拼团,抢购,默认,等可以通过动态组件实现,不用好多if语句判断了
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
异步组件应用:
去请求的时候
定义组件名
- kebab-case
-PasecalCase
注意:在使用自定义组件的时候,最好使用kebab-case的方式
基础组件的自动化全局注册:有些不解,希望有人指教
prop
注意:
在子组件中通过props定义名字的使用的驼峰,在运用绑定传递的时候需要用kebab-case
Vue.component('blog-post',{
props:['postTitle'],
templete:'<h3>{{postTitle}}</h3>'
})
//
<blog-post post-title="hello"></blog-post>
prop类型
当只在prop中写字符串的时候为数组类型
prop['title','likes','isPublished','commentIds','author']
当在prop中写类型的时候为对象类型
prop{
title:String,
likes:Number,
isPublished:Boolean,
commentIds:Array,
author:Object
}
prop的传递有静态类型和动态类型
//静态类型
<blog-post title="My journey with Vue"></blog-post>
//动态类型
<blog-post v-bind:title="post.title"></blog-post>
当传递一个静态类型的数字、对象、布尔类型的时候需要通过v-bind方式传递,告诉vue这不是一个字符串,这是一个javascript表达式
<blog-post v-bind:likes="42"></blog-post>
<blog-post is-published></blog-post>//默认传递的是true
<blog-post v-bind:is-published="false"></blog-post>
...
单向数据流
父级prop的更新会向下流动到子组件中,但是反过来不行。这样会防止子组件意外改变父级组件的状态。
子组件改变父组件的值需要通过this.$emit('值',方法名)的方式更改,在组件中:方法名:父组件在定义一个方法名
prop验证
Vue.component('my-component', {
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
注意:那些prop会在一个组件实例创建之前进行验证,所以实例属性如(data、computed等)再default或validator函数中不可用
自定义事件
.sync修饰符
在有些情况下,我们可能需要对一个prop进行“双向绑定”。也就是子组件可以修改父组件的问题
普通的this.$emit
myShow(){
this.$emit('fun','修改的值传递')
}
//父组件
<mycom v-on:fun="change"></mycom>
使用.sync修饰符
closeDiv() {
this.$emit('update:show', false); //触发 input 事件,并传入新值
}
//父组件
<myComponent :show.sync='valueChild' style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"></myComponent>
<button @click="changeValue">toggle</button>
//当前中的valueChild就是子组件传递过来的值,不用父组件在创建方法接收
export default{
data(){
return{
valueChild:true,
}
},
methods:{
changeValue(){
this.valueChild = !this.valueChild
}
}
}
注意:.sync后面不能是表达式
插槽:https://cn.vuejs.org/v2/guide/components-slots.html
处理边界情况
访问子组件实例,或子元素可以通过ref
<base-input ref="usernameInput"></base-input>
this.$refs.usernameInput
注意:refs
依赖注入
意思就是如果子元素想要访问父组件中的方法
在父组件中
provide: function () {
return {
getMap: this.getMap
}
}
在子组件中
inject: ['getMap']
注意:provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
程序化的事件监听
- $on(eventName,eventHandler)监听一个事件
- $once(eventName,eventHandler) 一次性监听一个事件
- $off(eventName,eventHander) 停止监听一个事件
应用场景:
使用第三方库的时候,例如时间插件
//平常我们使用
// 一次性将这个日期选择器附加到一个输入框上
// 它会被挂载到 DOM 上。
mounted: function () {
// Pikaday 是一个第三方日期选择器的库
this.picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'
})
},
// 在组件被销毁之前,
// 也销毁这个日期选择器。
beforeDestroy: function () {
this.picker.destroy()
}
潜在问题
需要在在这个组件实例中保存这个picker
难于程序化清理
通过程序化监听器解决,并且可以引用使用多个时间三方库
mounted: function () {
var picker = new Pikaday({
field: this.$refs.input,
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy', function () {
picker.destroy()
})
}
mounted: function () {
this.attachDatepicker('startDateInput') //不同的ref 元素,使用picker
this.attachDatepicker('endDateInput')
},
methods: {
attachDatepicker: function (refName) {
var picker = new Pikaday({
field: this.$refs[refName],
format: 'YYYY-MM-DD'
})
this.$once('hook:beforeDestroy', function () {
picker.destroy()
})
}
}
组件之间的循环引用
例子:
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"/>
</p>
tree-folder-contents 组件
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"/>
<span v-else>{{ child.name }}</span>
</li>
</ul>
需要在注册组件的时候这样引入:使用webpack的异步import
components: {
TreeFolderContents: () => import('./tree-folder-contents.vue')
}
组件之间循环引用的问题所在:(没看懂,请大佬指教)
- 为了解释这里发生了什么,我们先把两个组件称为 A 和 B。模块系统发现它需要 A,但是首先 A 依赖 B,但是 B 又依赖 A,但是 A 又依赖 B,如此往复。这变成了一个循环,不知道如何不经过其中一个组件而完全解析出另一个组件。为了解决这个问题,我们需要给模块系统一个点,在那里“A 反正是需要 B 的,但是我们不需要先解析 B。”
通过v-once 创建低开销的静态组件
ue.component('terms-of-service', {
template: `
<div v-once>
<h1>Terms of Service</h1>
... a lot of static content ...
</div>
`
})
注意:
当你需要渲染大量静态内容时,极少数的情况下它会给你带来便利,除非你非常留意渲染变慢了,不然它完全是没有必要的——再加上它在后期会带来很多困惑。例如,设想另一个开发者并不熟悉 v-once 或漏看了它在模板中,他们可能会花很多个小时去找出模板为什么无法正确更新。
混入
例子:
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data: function () {
return {
message: 'goodbye',
bar: 'def'
}
},
created: function () {
console.log(this.$data)
// => { message: "goodbye", foo: "abc", bar: "def" }
}
})
遇到冲突的data变量和方法名的策略
- 同名的data变量优先选择组件内部的
- 同名的钩子函数会合并成一个数组例如created 都会执行
var mixin = {
created: function () {
console.log('混入对象的钩子被调用')
}
}
new Vue({
mixins: [mixin],
created: function () {
console.log('组件钩子被调用')
}
})
// => "混入对象的钩子被调用"
// => "组件钩子被调用"
- 值为对象的选项,例如 method、components、 directives 将被合并为同一个对象。两个兑现键值对冲突的时候优先组件内部的
var mixin = {
methods: {
foo: function () {
console.log('foo')
},
conflicting: function () {
console.log('from mixin')
}
}
}
var vm = new Vue({
mixins: [mixin],
methods: {
bar: function () {
console.log('bar')
},
conflicting: function () {
console.log('from self')
}
}
})
vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "from self"
过滤器
<!-- 在双花括号中 -->
{{ message | capitalize }}
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
自定义过滤器:
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
或者
Vue.filter('capitalize', function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
})
new Vue({
// ...
})
当全局过滤器和局部过滤器重名时,采用局部过滤器
//过滤器可以串联
{{ message | filterA | filterB }}
//先将message传入到filterA方法中,然后再把筛选后的结果传入到filterB方法中进行筛选
还可以这样:
因为过滤器是javascript函数,因此可以接收参数
{{ message | filterA('arg1', arg2) }}
单元测试 (不明白,需要学习)
安全
第一原则:永远不要使用不可信任得模板
这样得做就等于允许在应用程序中执行任意得javascript,更糟得是如果在服务端渲染得话可能导致服务器被攻破。
例子:
new Vue({
el: '#app',
template: `<div>` + userProvidedString + `</div>` // 永远不要这样做
})
vue的安全措施:https://cn.vuejs.org/v2/guide/security.html
Html内容
不论使用模板还是渲染函数,内容都会被自动转义。
例如:
<h1>{{ userProvidedString }}</h1>
如果userProvideString变量中包含了
'<script>alert("hi")</script>' //被注入的恶意代码
则会被转义成
<script>alert("hi")</script> //没有办法执行script了
因此避免了脚本注入。该转义通过诸如textContent的浏览原生的Api完成,所以除非浏览器本身存在安全漏洞,否则不会存在安全漏洞
Attribute(属性)绑定 道理同
<h1 v-bind:title="userProvidedString">
hello
</h1>
如果 userProvidedString 包含了:
'" onclick="alert(\'hi\')'//恶意代码
则会被转义成为如下 HTML:
" onclick="alert('hi')
潜在危险
注入url
<a v-bind:href="userProvidedUrl">
click me
</a>
如果没有对url进行过滤“防止javascript:通过”则会由潜在的安全问题。有些库可以帮助解决
sanitize-url
https://www.npmjs.com/package/@braintree/sanitize-url
注意:在前端进行了url过滤,那么久已经有安全问题了。用户提供的url永远需要通过后端在入库之前进行过滤。然后这个被注入的问题在每个客户端连接api时就会被阻止,包括原生移动应用。
注入样式
<a
v-bind:href="sanitizedUrl"
v-bind:style="userProvidedStyles"
>
click me
</a>
假设sanitizedUrl已经被过滤过了。但通过userProvidedStyles,恶意用户仍然可以提供css来进行“点击诈骗”。将链接的样式设置为一个透明的方框覆盖在“登陆”上,然后再把按钮做成应用中的按钮的样子。它们就可以获取一个用户真实的登陆信息。
可以改为:
<a
v-bind:href="sanitizedUrl"
v-bind:style="{
color: userProvidedColor,
background: userProvidedBackground
}"
>
click me
</a>
推荐使用对象语法且只允许用户提供特定的可以安全控制的property的值
深入响应式原理
当把一个javascript对象传入vue实例作为data选项,vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转换为getter/setter.Object.defineProoperty是es5中一个无法shim的特性。是他们能够让vue追踪依赖。再属性被访问和修改时通知变更。
每一个实例都对应一个watcher实例,他会再组件渲染过程中把接触过的数据属性记录为依赖。之后当依赖的setter触发时,会通知watcher,从而使它关联的组件更新渲染。
检测变化的注意事项
Vue不能检测数组和对象的变化。
对象
vue无法检测property的添加和移除。
vue会在初始化实例时对属性执行getter/setter转化,所以属性必须在data上存在才能让vue将它转换为响应式。
对于已经创建得vue实例,vue不允许动态添加根级别得响应式
可以使用Vue.set(object,propertyName,value)
例如:
Vue.set(vm.someObject, 'b', 2)
或者
this.$set(this.someObject,'b',2)
有的时候可能需要为已有对象赋值多个新属性,比如使用Object.assign()或_.extend() 。但是这样添加到对象上得新属性不会触发更新。没有被监听到,添加这些属性。可以这样,用原对象与要混合进去得对象得属性一起创建一个新的对象。
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
数组
vue不能检测的数组变动:
1.当你利用索引直接设置一个数组项时:
vm.items[indexOfItem] = newValue
2.当你修改数组的长度时
vm.items.length = newLength
举例:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
可以触发更新的修改数组方式:
Vue.set(vm.items, indexOfItem, newValue)||vm.$set(vm.items, indexOfItem, newValue)
解决数组的长度
vm.items.splice(newLength)
异步更新队列
vue在更新dom时是异步执行的。只要侦听到数据变化,vue将开启一个队列,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被堆入到队列中一次。
vue在内部对异步队列尝试使用原生的 Promise.then 、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn,0)
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false(这个时候还没更改完成)
//只有在执行下一个事件循环tick中更新
Vue.nextTick(function () {
vm.$el.textContent === 'new message' // true
})
或者
Vue.component('example', {
template: '<span>{{ message }}</span>',
data: function () {
return {
message: '未更新'
}
},
methods: {
updateMessage: function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
this.$nextTick(function () {
console.log(this.$el.textContent) // => '已更新'
})
}
}
})
因为$necxtTick()返回一个Promise对象,所以你可以使用新的es7的语法糖
async/await语法完成相同的事情
async 声明一个异步的function
await 等待一个异步方法执行完成 再去执行下面的代码
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick() 等待
console.log(this.$el.textContent) // => '已更新'
}
}
在mounted钩子函数默认是this.$el.document获取不到
//可以这样
mounted: function () {
this.$nextTick(function () {
// 代码保证 this.$el 在 document 中
})
}
注意:
在created钩子中通过$on绑定了事件,最好在组件销毁前,清除事件的监听
beforeDestroy: function () {
eventHub.$off('add-todo', this.addTodo)
eventHub.$off('delete-todo', this.deleteTodo)
},
vue和其它框架的对比:https://www.jianshu.com/p/eef0a8e0dc49
vue打包得时候总是出现好多js,希望只出现一个js
因为页面比较多,打包成一个js的话,文件会很大。否则第一次加载的时候半天都是白屏,一般都是按需加载,打包也是根据需要进行打包