vue新建项目
1.全局安装vue-cli:npm install --global vue-cli
2.新建vue项目:vue init webpack 项目名称
###命名 1.组件名,官方推荐的组件名是 每个单词首字母大写(PascalCase) 或者 全小写用 - 连接(kebab-case)。 在DOM中使用的时候, 改为全小写, 单词之间用 - 连接。
Vue.component('MyComponent', {});
或者
Vue.component('my-component', {});
import MyComponent from 'MyComponent.vue';
export default {
name: 'MyComponent'
}
在DOM(即非字符串的模板) 中使用的时候,
<my-component></my-component>
2.props 声明 prop的时候, 使用驼峰命名(myProps), 模板中使用的时候, 用 - 连接(my-props)
props: {
myProps: {}
}
<my-component :my-props="abc"></my-component>
3.自定义事件名 因为html对大小写不敏感,所以大写的都会被转为小写的。 所以推荐 都用 -连接来命名。
this.$emit('my-event');
<my-component @my-event="abc"></my-component>
数据与方法
当一个 Vue 实例被创建时,它将data对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。 值得注意的是只有当实例被创建时就已经存在于data中的 property 才是响应式的
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true
// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2
// ……反之亦然
data.a = 3
vm.a // => 3
html:
<body>
<div id="app">{{a}} {{b}}</div>
</body>
js:
import Vue from 'vue'
let data = {a:'ee'}
let vm = new Vue({
el: '#app',
data,
})
console.log(vm.a) // ee
console.log(vm.b) // undefined
vm.a = 'changed'
vm.b = 'bchanged'
// 页面显示:achanged bchanged
改为异步:(Promise或者setTimeout)
import Vue from 'vue'
let data = {a:'ee'}
// 顶层元素,其他页面页面通过路由+组件的形式引入
let vm = new Vue({
el: '#app',
data,
})
console.log(vm.a) // ee
console.log(vm.b) // undefined
vm.a = 'changed'
setTimeout(() => {
vm.b = 'bchanged'
}, 1)
// 页面显示:achanged
那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个 property,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:
data: {
b: '',
visitCount: 0,
hideCompletedTodos: false,
todos: [],
error: null
}
修改代码
import Vue from 'vue'
let data = {a:'ee', b:''}
let vm = new Vue({
el: '#app',
data,
})
console.log(vm.a) // ee
console.log(vm.b) // undefined
vm.a = 'changed'
setTimeout(() => {
vm.b = 'bchanged'
}, 1)
// 页面显示:achanged bchanged
这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。
生命周期
import Vue from 'vue'
let data = {a:'aVal', b: 'bVal'}
let vm = new Vue({
el: '#app',
data,
beforeCreate: function () {
// `this` 指向 vm 实例
console.log('beforeCreate-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
},
created: function () {
console.log('created-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
},
beforeMount: function () {
console.log('beforeMount-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
},
mounted: function () {
console.log('mounted-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
},
beforeUpdate: function () {
console.log('beforeUpdate-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
},
updated: function () {
console.log('updated-----a is: ' + this.a + ', b is:' + this.b, document.getElementById('app').innerText);
}
})
vm.a = 'changed'
new Promise((resolve) => {
resolve();
}).then(() => {
console.log('set b in promise')
vm.b = 'bchanged in promise'
})
console.log('mounted');
执行结果:
// beforeCreate-----a is: undefined, b is:undefined {{a}}--{{b}}
// created-----a is: aVal, b is:bVal {{a}}--{{b}}
// beforeMount-----a is: aVal, b is:bVal {{a}}--{{b}}
// mounted-----a is: aVal, b is:bVal aVal--bVal
// mounted
// beforeUpdate-----a is: changed, b is:bVal aVal--bVal
// updated-----a is: changed, b is:bVal changed--bVal
// set b in promise
// beforeUpdate-----a is: changed, b is:bchanged in promise changed--bVal
// updated--a is: changed, b is:bchanged in promise changed--bchanged in promise
[自我总结]:
created: 针对设置data数据
mounted: 针对替换el节点
updated: 针对data数据变化
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。 额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。 这里有两种常见的试图变更一个 prop 的情形: 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性
<body>
<div id="app">
<!-- a、父组件通过属性绑定的方式传值 -->
<counter :count="0"></counter>
<counter :count="1"></counter>
</div>
<script>
var counter={
//b、子组件接收父组件的传值
props:['count'],
//c、把父组件传来的值放入模板中渲染
template:"<div @click='handleClick'>{{number}}</div>",
data:function(){
return{
//复制一份,子组件就可以修改了
number:this.count
}
},
methods:{
handleClick:function(){
//不能直接这么写,因为子组件不能修改父组件的传值
// this.count++
this.number++
}
}
}
var app = new Vue({
el:'#app',
//注册局部组件
components:{
counter:counter
}
})
</script>
</body>
除了数据 property(data上的属性),Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 data-实例属性、vm.$watch-实例方案), 以便与用户定义的 property 区分开来,参考:API 参考
模板语法
文本:
{{msg}}
原始HTML(XSS攻击)
<div id="app">
<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
</div>
属性:html节点的有效属性
v-bind:属性
<div v-bind:id="dynamicId"></div>
// 缩写
<div :id="dynamicId"></div>
方法:html节点的有效方法,在method中定义此方法
v-on:方法
<div v-on:click="doSomething"></div>
// 缩写
<div @click="doSomething"></div>
动态参数:[],如空格和引号无效
<a v-on:[eventName]="doSomething"> ... </a>
使用 JavaScript 表达式
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
<!-- 这是语句,不是表达式 不支持-->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 不支持 -->
{{ if (ok) { return message } }}
修饰符(补充,缩小范围)
修饰符 (modifier) 是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
修饰符包括,事件修饰符、按键修饰符、系统键盘修饰符、系统键组合修饰符、鼠标操作键修饰符等
<form v-on:submit.prevent="onSubmit">...</form>
计算属性(computed) VS 方法(method) VS 侦听属性(watch)
计算属性:基于它们的响应式依赖进行缓存的,响应式依赖的数据变动而触发, 依赖属性(可多个)
侦听属性:基于它们的响应式依赖进行缓存的,响应式依赖的数据变动而触发,侦听具体属性(1个)
方法:事件触发时调用
<div id="app">
firstName:<input v-model='firstName'/><br>
lastName:<input v-model='lastName'/><br>
fullName: {{fullName}}
</div>
watch代码:
import Vue from 'vue'
let data = {
firstName: 'Foo',
lastName: 'Bar'
}
new Vue({
el: '#app',
data,
methods: {
clickTry: function () {
console.log('aaa')
}
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
computed代码:
import Vue from 'vue'
let data = {
firstName: 'Foo',
lastName: 'Bar'
}
new Vue({
el: '#app',
data,
methods: {
clickTry: function () {
console.log('aaa')
}
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
二者不同点:初始化时,watch代码fullName显示为空,computed代码正常
Class 与 Style 绑定
基本思想:Merge 1.对象语法
// data: {
// isActive: true,
// hasError: false
// }
<div
class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>
结果:
<div class="static active"></div>
2.数组语法
// data: {
// activeClass: 'active',
// errorClass: 'text-danger'
// }
<div v-bind:class="[activeClass, errorClass]"></div>
结果:
<div class="active text-danger"></div>
3.用在组件上(略)
条件渲染
v-if v-else-if v-else
v-show(切换display)
v-if 用 key 管理可复用的元素. 有key, 这两个元素是完全独立的,不要复用它们”
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
列表渲染
v-for
// data: {
// object: {
// title: 'How to do lists in Vue',
// author: 'Jane Doe',
// publishedAt: '2016-04-10'
// }
// }
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
组件
表单组件
v-model:它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理,包括
<input><select><<textarea>
v-model 会忽略所有表单元素的 value、checked、selected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。
v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
text 和 textarea 元素使用 value(v-model) property 和 input 事件;
checkbox 和 radio 使用 checked property 和 change 事件;
select 字段将 value(v-model) 作为 prop 并将 change 作为事件。
子组件
子组件通过props获取父组件传递过来的值 子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件
这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
动态组件
Vue 的 元素加一个特殊的 is attribute 来实现:
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
代码示例:codesandbox.io/s/github/vu…
<!DOCTYPE html>
<html>
<head>
<title>Dynamic Components Example</title>
<script src="https://unpkg.com/vue"></script>
<style>
.tab-button {
padding: 6px 10px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border: 1px solid #ccc;
cursor: pointer;
background: #f0f0f0;
margin-bottom: -1px;
margin-right: -1px;
}
.tab-button:hover {
background: #e0e0e0;
}
.tab-button.active {
background: #e0e0e0;
}
.tab {
border: 1px solid #ccc;
padding: 10px;
}
</style>
</head>
<body>
<div id="dynamic-component-demo" class="demo">
<button
v-for="tab in tabs"
v-bind:key="tab"
v-bind:class="['tab-button', { active: currentTab === tab }]"
v-on:click="currentTab = tab"
>
{{ tab }}
</button>
<component v-bind:is="currentTabComponent" class="tab"></component>
</div>
<script>
Vue.component("tab-home", {
template: "<div>Home component</div>"
});
Vue.component("tab-posts", {
template: "<div>Posts component</div>"
});
Vue.component("tab-archive", {
template: "<div>Archive component</div>"
});
new Vue({
el: "#dynamic-component-demo",
data: {
currentTab: "Home",
tabs: ["Home", "Posts", "Archive"]
},
computed: {
currentTabComponent: function() {
return "tab-" + this.currentTab.toLowerCase();
}
}
});
</script>
</body>
</html>
解析 DOM 模板时的注意事项
// 有些 HTML 元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。
// 这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:
<table>
<blog-post-row></blog-post-row>
</table>
//这个自定义组件 <blog-post-row> 会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的 is attribute 给了我们一个变通的办法:
<table>
<tr is="blog-post-row"></tr>
</table>
需要注意的是如果我们从以下来源使用模板的话,这条限制是不存在的:
1.字符串 (例如:template: '...')
2.单文件组件 (.vue)
3.如下形式:
<script type="text/x-template">
单文件组件
<!-- my-component.vue -->
<template>
<div>This will be pre-compiled</div>
</template>
// 或如下图内联
<script src="./my-component.js"></script>
// 或如下图内联
<style src="./my-component.css"></style>
检测变化的注意事项
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。
- 对于对象,Vue 无法检测property 的添加或移除。
<div id="app">
firstName:<input v-model='name.firstName'/><br>
lastName:<input v-model='name.lastName'/><br>
fullName: {{fullName}}
<div @click="clickTry">clickTry</div>
</div>
import Vue from 'vue'
let data = {
name: {
firstName: 'Foo'
},
canShow: true
}
window.vm = new Vue({
el: '#app',
data,
methods: {
clickTry: function () {
// 此场景
this.name = Object.assign(this.name, {lastName: 'aaa' })
}
},
computed: {
fullName: function () {
return this.name.firstName + ' ' + this.name.lastName
}
}
})
执行结果:触发clickTry,未改变fullName值。(lastName是非响应式的) 原因,在初始化时未对lastName绑定检测
分析&解决方案:绑定监测(defineProperty)的实际:
a. 初始化initInjections时循环调用defineReactive$$1
b. Vue.set(this.name, 'lastName', 'aa')
c. this.$set(this.name, 'lastName', 'aa')
d. compiler后的input(v-model)的方法会调用defineReactive$$1
e. 改变父对象,重新绑定子对象
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
- 对于数组,Vue 不能检测以下数组的变动:
a. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue b. 当你修改数组的长度时,例如:vm.items.length = newLength
分析:Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括 push() pop() shift() unshift() splice() sort() reverse()
解决方案:
a.Vue.set(vm.items, indexOfItem, newValue) b.vm.$set(vm.items, indexOfItem, newValue) c.vm.items.splice(indexOfItem, 1, newValue) d.替换数组