什么是Vue.js
Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API;
Vue.js是一个构建数据驱动的Web界面的库。
Vue.js是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue生态系统支持的库开发的复杂单页应用。数据驱动+组件化的前端开发。
简而言之:Vue.js是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。
什么是 mvvm?
MVVM 是 Model-View-ViewModel 的缩写。mvvm 是一种设计思想。Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。
简述Vue的响应式原理
当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为 getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。
每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
Vue.js特点
简洁:页面由HTML模板+Json数据+Vue实例组成
数据驱动:自动计算属性和追踪依赖的模板表达式
组件化:用可复用、解耦的组件来构造页面
轻量:代码量小,不依赖其他库
快速:精确有效批量DOM更新
模板友好:可通过npm,bower等多种方式安装,很容易融入
开始学习
首先我是直接从写网页开始练习的,随便创建一个HTML文件,然后在中间直接用<script>
引入
CDN
对于制作原型或学习,你可以这样使用最新版本:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
或者用一个链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
然后就能开始练习了,我是在这里学习的
遇到的问题
学习过程中发现
Elements in iteration expect to have ‘v-bind:key’ directives的报错
原因:VSCode中ESLint的功能,对vue进行了ESLint检查。
解决方法:File=>Preferences=>Settings,然后在搜索框搜索vetur.validation.template,将vetur.validation.template从true改为false。
组件相关
data必须是一个函数
组件中我定义的data是如下的,但是,毫无作用。
data: {
count: 0
}
改成这样才有用
data: function () {
return {
count: 0
}
}
因为一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:否则就会出现其中一个组件修改了,其他组件也修改的事了。参考这里
重构模板
<div id="lordnb">
<lord-nb v-for="post in posts" v-bind:key="post.id" v-bind:name="post.name" v-bind:father="post.father" v-bind:content="post.content"></lord-nb>
</div>
<script>
Vue.component("lord-nb",{
props : {
id : Number,
name : String,
father : {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { name: '黄帝' }
}
},
content : {
type: String,
default :'<span style=\'color:yellow\'>黄色</span>'
}
},
template :'<div class="blog-post"><h3>id:{{id}},name:{{name}},father:{{father.name}}</h3><div v-html="content"></div></div>'
})
var father2={
name : "赵老大"
}
var lordnb = new Vue({
el : '#lordnb',
data: {
posts: [
{ id: 1, name: '赵', father : father2, content : '<span style=\'color:red\'>红色</span>'},
{ id: 2, name: '钱' },
{ id: 3, name: '孙' },
{ id: 4, name: '李' }
], content : '<span style=\'color:blue\'>蓝色</span>'
}
})
</script>
如果想重构一下,觉得组件变得越来越复杂的时候改成这样
<div id="lordnb">
//删减了其他,只保留了key,并增加了post
<lord-nb v-for="post in posts" v-bind:key="post.id" v-bind:post="post"></lord-nb>
</div>
Vue.component("lord-nb",{
props : {
//增加一个post
post : Object,
id : Number,
name : String,
father : {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { name: '黄帝' }
}
},
content : {
type: String,
default :'<span style=\'color:yellow\'>黄色</span>'
}
},
//template中所有属性增加前缀post.
template :'<div class="blog-post"><h3>id:{{post.id}},name:{{post.name}},father:{{post.father.name}}</h3><div v-html="post.content"></div></div>'
})
然而此时,我发现了,浏览器会报错
Error in render: "TypeError: Cannot read property ‘name’ of undefined"
只有把father:{{ post.father.name }}
改成father:{{father.name}}
,
并且把
<lord-nb v-for="post in posts" v-bind:key="post.id" v-bind:post="post"></lord-nb>
增加为
<lord-nb v-for="post in posts" v-bind:key="post.id" v-bind:post="post" v-bind:father="post.father"></lord-nb>
时,错误提示才会去掉,且界面显示才正常。
具体原因不清楚,只因为我的这个father是对象?props一般好像支持值类型的比较多,对象一般用watch?我也不清楚,我初学者。
非 Prop 的特性
根据文中所说,大概意思就是说
Vue.component('good-title', {
template : '<h3>标题</h3>',
mounted: function () {
console.log(this.$el.getAttribute('title-name'))
}
});
如上所述,这里没props。然后在HTML中定义一个叫做title-name的属性,然后这个 title-name=“title” 特性就会自动添加到 的根元素上。
<good-title title-name="title"></good-title>
但是这样其实并不会在控制台显示title。
必须增加
<div id="sss">
<good-title title-name="title"></good-title>
</div>
var sss = new Vue({
el : '#sss'
})
此时,才会调用console.log(this.$el.getAttribute('title-name'))
控制台才会显示title。为什么用mounted,而我用created页面会报错,具体参考Vue生命周期中mounted和created的区别
监听子组件事件
之前都是各种事件相关的都在模板里就定义好了,但如果想从组件里控制子元素中的事件,或者说一个组件集合控制子组件中的事件。参考这里
<blog-post :style="{ fontSize: postFontSize + 'em' }" id="enlarge" v-bind:title="title" v-bind:content="content"
v-on:click="postFontSize += 0.1"></blog-post>
Vue.component('blog-post', {
props: ['content','title'],
template: `
<div class="blog-post">
<span>{{ title }}</span>
<button>
Enlarge text
</button>
<div v-html="content"></div>
</div>
`
})
var enlarge = new Vue({
el:'#enlarge',
data :{
title : 'qwe',
content : '<span>dasdadasd</span>',
postFontSize: 1
}
})
然而按钮怎么点都没用,这是因为我们定义的元素是div,事件也被定义到这个div中,而按钮是div的子元素,这个事件应该被定义到这个button中。那么该怎么做呢?
子元素可以通过调用内建的 $emit 方法 并传入事件名称来触发一个事件。
//这里定义了v-on:enlarge-text替代了v-on:click
<blog-post :style="{ fontSize: postFontSize + 'em' }" id="enlarge" v-bind:title="title" v-bind:content="content"
v-on:enlarge-text="postFontSize += 0.1"></blog-post>
//下面button用v-on:click="$emit('enlarge-text')"将enlarge-text这个事件传到父组件blog-post中
Vue.component('blog-post', {
props: ['content','title'],
template: `
<div class="blog-post">
<span>{{ title }}</span>
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
<div v-html="content"></div>
</div>
`
})
var enlarge = new Vue({
el:'#enlarge',
data :{
title : 'qwe',
content : '<span>dasdadasd</span>',
postFontSize: 1
}
})
在组件上使用 v-model
v-model是一个指令,限制在<input>、<select>、<textarea>、components
中使用,修饰符.lazy(取代 input 监听 change 事件)、.number(输入字符串转为有效的数字)、.trim(输入首尾空格过滤)。它其实是一个语法糖。
<input v-model="searchText">
等价于
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
那么在组件上怎么使用v-model呢?
一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的event。
<my-input id="myinput" v-model="searchText" v-bind:label = "mylabel"></my-input>
Vue.component("my-input",{
props : ['label','value'],
template : `
<div>{{label}}
<input v-bind:value="value" v-on:input="$emit('input', $event.target.value)">
<p>{{ value }}</p>
</div>
`
})
new Vue({
el: '#myinput',
data : {
mylabel : '姓名',
searchText : ''
}
})
通过插槽分发内容
根据这里,我怎么也弄不出它的效果,实际只需要增加样式就够了。
<style>
.demo-alert-box {
padding: 10px 20px;
background: #f3beb8;
border: 1px solid #f09898;
}
</style>
methods与computed 的区别
computed:计算属性一般在数据量比较大,比较耗时的情况下使用(例如搜索),只有虚拟dom与真实dom不同的情况下会执行computed;
method:方法使用,如果使用了其中一个方法,其他的方法都会被执行,比较耗时,不管虚拟dom与真实dom一不一样都会执行。
<div id="lord">
<p>{{ reversedMessageMethod() }}</p>
<p>{{ reversedMessageComputed }}</p>
</div>
new Vue({
el: '#lord',
data: data,
methods:{
reversedMessageMethod: function () {
return this.message1.split('').reverse().join('')
}
},
computed : {
reversedMessageComputed: function () {
return this.message2.split('').reverse().join('')
}
}
})
注意:computed中的属性不能与data中的属性同名,否则会报错。
区别:computed计算的结果如果不发生改变就不会触发result这个函数。而methods中一般都是定义的需要事件触发的一些函数。每次只要触发事件,就会执行对应的方法。如果把computed中的方法写到method中会浪费性能。computed必须返回一个值页面绑定的才能取得值,而methods中可以只执行逻辑代码,可以有返回值,也可以没有。