Vue 响应式的前端视图层框架
库:定义方法进行调用
框架:框架中约定的规范进行方法实例化,给模版进行操作
两个简单的demo
<div id="app">
<input type="text" v-on:input='inputCb'>
<div>{{ message }}</div>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello world!'
},
methods: {
inputCb(e) {
this.message = e.target.value;
}
}
})
</script>
Vue 引入
- 通过CDN: 包含了vue中所有功能,适合小型项目或部分使用vue 1.1 引用全部vue.js,运行时编译及渲染 1.2 引用部分vue.js,仅引入渲染部分
<div id="app">
<input type="text" v-model="message">
<div>The message is {{ message }}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello world!'
}
})
</script>
运行时的编译:
加载之后 首先显示html,加载过 vue 之后,模版才会被编译成内部可以识别的变量,再通过data 渲染出来
这种形式需要引入全部的vue.js
优化:不进行运行时的编译而是定义一个render方法
<div id="app">
<!-- 嵌套解构中 message
<div>
<span>{{ message }}</span>
</div> -->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello world!'
},
render(createElement) {
return createElement(
'div',
null,
[createElement('span', this.message)])
}
})
</script>
静态编译方法后,只需要引入vue.runtime.js,在vue cli当中会静态编译
- 通过Vue Cli: 包含了所有的webpack功能
Vue 中的细节
- 模版中的 {{}} 只接受表达式
什么是表达式? expression & statement
expression: 一般指单独的一段可运行代码,一般具有返回值 statement: 一般指多个表达式的集合,也具有上下文的语义
一般在JS中常见
- 赋值表达式 如:a = 5 返回值就为5,如果加了分号则为语句 a = 5;
- 逗号表达式 1,2,3
- 布尔表达式
- 函数表达式(匿名函数)
- 三目运算符
// 函数表达式
var a = function () {}
// 函数语句
function b() {}
- 标签中的新属性 v-...
- v-bind: 我们能将 data 中的值绑定到当前属性中,可简写为 :
<div v-bind:title='message'>我绑定了 title 为 {{ message }}</div>
- v-on: 能够绑定实例中配置的事件,可简写为 @
<input type="text" v-on:input='inputCb'>
<script>
methods: {
inputCb(e) {
this.message = e.target.value;
}
}
</script>
- v-for: 列表级别渲染,迭代渲染所有子元素,渲染多个当前标签元素(需要写在当前的循环体上,可以使用在数组及对象上) 在对象循环中,浏览器循环的 key 和 value 的顺序可能会不同
<ul>
<li v-for="(item, index) in items1">
{{ item.value }} - {{ index }}
</li>
</ul>
<ul>
<li v-for="(value, key) in items2">
{{ value }} - {{ key }}
</li>
</ul>
items1: [{
value: 1
}, {
value: 2
}],
items2: {
key1: 'value1',
key2: 'value2',
}
- v-if/v-else/v-show: 控制自元素视图显隐
<div v-if="message.length >=5">这里的message 长度 大 于5</div>
<div v-else="message.length < 5">这里的message 长度 小 于5</div>
v-if 和 v-show 区别
v-show 是通过css display: none来实现显示隐藏;而v-if是通过添加或删除该dom节点实现显示和隐藏
- v-model: 应用于表单,创建与控元素的双向绑定
- v-html: 将最终值的结果渲染为html
- v-text: 等同于直接在文本处使用 {{ x }}
计算属性
大部分时候,我们在模版也就是 html 中写表达式会让模版变得复杂,所以我们可以通过计算属性来简化模版。
但大多数时候,我们也可以通过定义方法的形式来直接在表达式内部调用函数,不过计算属性也可以模拟出使用参数的形式。
// 使用computed
<div v-if="validLength">这里的message 长度 大 于5</div>
<div v-else="message.length < 5">这里的message 长度 小 于5</div>
computed: {
validLength() {
return this.message.length >=5
}
},
// 使用methods
<div v-if="validLengthFn()">这里的message 长度 大 于5</div>
<div v-else="message.length < 5">这里的message 长度 小 于5</div>
methods: {
validLengthFn() {
return this.message.length >=5
}
}
methods 与 计算属性 computed
methods 只要当前组件重新渲染了,对于函数来说都会调用一次
改变message 的值,触发重新渲染,那么调用函数没问题;但是改变message2 的值,触发了整个组件的重新渲染,那么这时候也会触发methods
methods: {
validLengthFn() {
console.log('validLengthFn trigger')
return this.message.length >=5
}
使用 computed:改变 message 的值,触发重新渲染;但改变 message2 的值,虽然整个组件重新渲染了,但是 computed 依赖的值没有边,因此不会触发
computed: {
validLength() {
console.log('validLength trigger')
return this.message.length >=5
}
computed 和 methods 区别:
- computed 计算属性利用对象属性的getter,表现形式为一个变量赋值,故而无法传值,而methods 可以传值
- 使用 computed 是惰性求值,依赖的值不发生变化不会重新计算,但是函数执行一次计算一次
vue.js 组件
上面示例中的 new Vue 实例化之后,在具体的元素上实例化了根节点。对于框架而言,一般都会给用 户提供一种复用的方式,而在 vue 中,我们可以通过定义组件的方式,来实现模版的复用来减少代码。
通常通过在实例上面定义 component 属性来实现
组件的 data 为一个函数,彼此间的 data 不会受影响
<div id="app">
<hello-world></hello-world>
<hello-world></hello-world>
<hello-world></hello-world>
</div>
Vue.component('hello-world', {
data: function () {
return {
message: 'Hello World!'
}
},
template: '<p> {{ message }} </p>'
})
new Vue({
el: '#app'
});
组件间数据传递
-
父 -> 子
1.1 父组件自定义属性,传递数据变量到子组件
<blog-post :post="post">
data () { return { post: [ { id: 1, title: 'My journey with Vue', context: 'How fun it is!!' }, { id: 2, title: 'Blogging with Vue', context:'What a frameworks!' }, { id: 3, title: 'Why Vue is so fun', context: 'I don\'t think so!' } ], } }
1.2 子组件通过props 属性进行接受并使用
<div v-for="item in post" :key="item.id"> <h3>{{ item.title }}</h3> <p>{{ item.context }}</p> </div>
props: { post: Array }
-
子 -> 父
2.1 子组件 emit 一个自定义事件给父组件
<button @click="$emit('enlarge-size', 0.1)">Enlarge FontSize</button>
2.2 父组件进行监听并使用
<blog-post :post="post" :style="{ fontSize: postFontSize + 'em'}" @enlarge-size="postFontSize += $event" />
有状态组件与无状态组件 (states & status)
大部分时候,我们需要区分一些具有副作用的组件,例如某些组件我们需要发送 ajax 请求之后渲染一些 数据,这时候我们就需要将这部分数据内容进行一个区分,推荐做法将 UI 部分渲染于子组件中,做一个只通过传入数据渲染的无状态组件,而在有副作用组件中维护所有的数据。
为什么列表渲染需要key?
列表子元素都是一样的,框架本身不希望频繁渲染列表,需要明白每个子元素所在的为止,比如更新某个子元素,很难区分整个列表都需要更新还是只更新某个;这时候就需要key,相当于列表组件中的唯一标识,当改变时具体更新哪一个内容。
<hello-world v-for="item in items" :item="item" :key="item.id"></hello-world>
Vue.component('hello-world', {
// data: function () {
// return {
// message: 'Hello World!'
// }
// },
props: ['item'],
template: '<p> {{ item.title }} </p>'
})
new Vue({
el: '#app',
data: {
items: [
{id: 1, title: 'item1'},
{id: 2, title: 'item2'},
{id: 3, title: 'item3'},
]
}
});
上述例子中,items 模拟后台获取数据,对于new Vue组件来说,他是一个有副作用的有状态组件,需要在其生命周期中获取一部分数据再去渲染;但对于hello world中来说,只需要从父组件中获取item对象内容,渲染出对于数据的值,自己内部不需要维护 data 就是一个单纯的 UI 组件,为一个无状态组件
在实际工作中,有意识分割有副作用的组件和无副作用的组件,尽量可以把副作用集中在一个组件中,同时渲染出无状态的 纯 UI 组件
生命周期 - 不同时机定义方法
什么时候会发送请求?
生命周期中,只有 beforeCreate 和 created 两个生命周期函数会在服务端渲染 vue 组件时用到,只需要在mounted 周期内发送数据,beforeCreated 和 created 发送数据有可能造成冲突 将el 外部的HTML作为 template 编译?? 相当于调用outerHTML,即包含
<div id='app'>