文章目录
Vue实例
安装完vue-loader后,必须,
npm installl --save-dev vue-template-compiler
- 在webpack配置文件的
plugins
字段添加VueLoaderPlugin
const {VueLoaderPlugin} = require("vue-loader");
module.exports = {
plugins:[
new VueLoaderPlugin()
]
}
webpack打包时,默认加载的是vue/dist/vue.runtime.esm.js
,但是如果代码里有字符串模板,则需要编译器将字符串模板编译成render函数,因此需要使用完整版本,即vue/dist/vue.esm.js
。
此时在webpack.config.js中添加如下配置即可。
module.exports = {
resolve:{
"alias":{
"vue$":"vue/dist/vue.esm.js"
}
}
}
比如,
<body>
<div id="root">
<p>{{message}}</p>
</div>
</body>
import Vue from "vue";
const vm = new Vue({
el:"#root",
data:{
message:"hello world"
}
})
如果直接使用vue的运行时版本,就会遇到这个问题:
vue.runtime.esm.js:638 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
这时,使用以上方法就可以解决。
选项对象
const vm = new Vue({
//选项
})
创建一个vue实例,并传入选项对象。
<body>
<div id="root">
<div>{{a}},{{b}}</div>
</div>
</body>
import Vue from "vue";
const data = {
a:"hello",
b:"world"
}
const vm = new Vue({
el:"#root",
data:data
});
console.log(vm.$data === data);
console.log(vm.$el);
vm.$watch("a",function(newValue,oldValue){
if(newValue!==oldValue){
console.log(`a is changed to be '${newValue}'`);
}
})
data.a = "hi"
- 在选项属性或者回调函数中,慎用箭头函数,因为
this
data
data
对象中的属性会成为Vue实例vm
的属性,所以可以访问vm.a
、vm.b
。
<body>
<div id="root">
<div>{{a}},{{b}}</div>
</div>
</body>
import Vue from "vue";
const vm = new Vue({
el:"#root",
data:{
"a":"hello",
"b":"world"
}
});
console.log(vm.a);
console.log(vm.b);
computed
- 计算属性也会成为Vue实例
vm
的属性
计算属性reversedMessage
成为了Vue实例vm
的属性,所以可以访问vm.reversedMessage
。
<body>
<div id="root">
<div>{{message}}</div>
<div>{{reversedMessage}}</div>
</div>
</body>
import Vue from "vue";
const vm = new Vue({
el:"#root",
data:{
"message":"hello world"
},
computed:{
"reversedMessage":function(){
return this.message.split("").reverse().join("");
}
}
});
console.log(vm.message);
console.log(vm.reversedMessage);
- 计算属性默认只有getter,如果需要也可以提供setter
<body>
<div id="root">
<div>{{fullname}}</div>
</div>
</body>
import Vue from "vue";
const vm = new Vue({
el:"#root",
data:{
"firstname":"Steve",
"lastname":"Jobs"
},
computed:{
"fullname":{
get:function(){
return this.firstname+" "+this.lastname;
},
set:function(val){
const [firstname,lastname] = val.split(" ");
this.firstname = firstname;
this.lastname = lastname;
}
}
}
});
vm.fullname = "Tomas Edison"
watch
<body>
<div id="root">
<div><input type="text" v-model="content"></div>
<div>content is {{content}}</div>
</div>
</body>
import Vue from "vue";
const vm = new Vue({
el:"#root",
data:{
"content":""
},
watch:{
"content":function(newValue,oldValue){
console.log(`content is '${newValue}'`);
}
}
});
指令
参数
v-bind:参数
,如v-bind:title
,将元素的title
属性与表达式text
绑定
<body>
<div id="root">
<div v-bind:title="text">hi</div>
</div>
</body>
import Vue from "vue";
const vm = new Vue({
el:"#root",
data:{
text:"hello world"
}
});
v-bind:disabled
,将元素的disabled
属性与表达式isDisabled
绑定。
<body>
<div id="root">
<button v-bind:disabled="isDisabled">click me</button>
</div>
</body>
import Vue from "vue";
const vm = new Vue({
el:"#root",
data:{
isDisabled:true
}
});
动态参数
动态参数名最好全小写,如有必要,短横线分隔命名(kebab-case
),如v-bind:[titlename]
、v-on:[eventname]
,但不要写成v-bind:[titleName]
、v-on:[eventName]
,因为浏览器在解析html时,会将元素属性名全部转换成小写。
组件
组件是可复用的Vue实例,所以它可以接受和new Vue()
相同的选项对象,比如data
、computed
、watch
、methods
、生命周期钩子等。唯一的例外就是el
,它是根实例特有的。
组件名
放在html里的组件名最好 全部小写,如有必要,短横线分隔命名(kebab-case
),因为浏览器在解析html标签时会将大写字母转换成小写字母。看个例子。
<body>
<div id="root">
<MyComponent></MyComponent>
</div>
</body>
import Vue from "vue";
Vue.component("MyComponent",{
template:"<div>hello world</div>"
})
const vm = new Vue({
el:"#root"
})
针对以上问题,以下任一种方式都可以解决。
组件的data必须是一个函数
根实例的data
是一个对象,但组件的data
必须是一个函数
每个组件必须只有一个根元素
父组件通过props向子组件传递数据
props名
html里的props名最好全小写,如有必要,短横线分隔命名(kebab-case
),因为浏览器在解析html时,会将props名从大写字母转换成小写字母。看个例子。
<body>
<div id="root">
<Counter initValue="10"/>
</div>
</body>
import Vue from "vue";
Vue.component("Counter",{
template:"<div>{{initValue}}</div>",
props:["initValue"]
})
const vm = new Vue({
el:"#root"
})
针对以上问题,以下任一写法皆可。
动静态props
props分静态和动态。
- 静态props和普通的DOM属性差不多,比如
title
,静态props的值总是一个字符串
<body>
<div id="root">
<my-component a="hello" b="world"/>
</div>
</body>
import Vue from "vue";
Vue.component("my-component",{
template:'\
<div>\
<div>{{a}}</div>\
<div>{{b}}</div>\
<div>{{a+b}}</div>\
</div>\
',
props:["a","b"]
})
const vm = new Vue({
el:"#root"
})
- 动态props就是
v-bind:名称
,如v-bind:title
,动态props的值可以是一个数值、布尔值、字符串、数组、对象等任意类型
<body>
<div id="root">
<my-component v-bind:a="10" v-bind:b="20"/>
</div>
</body>
import Vue from "vue";
Vue.component("my-component",{
template:'\
<div>\
<div>{{a}}</div>\
<div>{{b}}</div>\
<div>{{a+b}}</div>\
</div>\
',
props:["a","b"]
})
const vm = new Vue({
el:"#root"
})
<body>
<div id="root">
<my-component
v-bind:num="10"
v-bind:is-need="false"
v-bind:list="[1,2,3]"
v-bind:obj="{a:'hello',b:'world'}"
/>
</div>
</body>
import Vue from "vue";
Vue.component("my-component",{
template:'\
<div>\
<div>{{num}} is {{Object.prototype.toString.call(num)}}</div>\
<div>{{isNeed}} is {{Object.prototype.toString.call(isNeed)}}</div>\
<div>{{list}} is {{Object.prototype.toString.call(list)}}</div>\
<div>{{obj}} is {{Object.prototype.toString.call(obj)}}</div>\
</div>\
',
props:["num","isNeed","list","obj"]
})
const vm = new Vue({
el:"#root"
})
v-bind
传递一个对象的所有属性
<body>
<div id="root">
<todo-list></todo-list>
</div>
</body>
import Vue from "vue";
Vue.component("todo-list",{
template:'<ul>\
<todo-item \
v-for="item in list"\
v-bind:key="item.id"\
v-bind="item">\
</todo-item>\
</ul>',
data:function(){
return {
list:[
{id:"#1",title:"吃饭",isDone:true},
{id:"#2",title:"睡觉",isDone:true},
{id:"#3",title:"打豆豆",isDone:false},
]
}
}
});
Vue.component("todo-item",{
props:["title","isDone"],
template:'<li>\
<input type="checkbox" v-model="isDone">\
<span>{{title}}</span>\
</li>'
});
const vm = new Vue({
el:"#root"
})
props验证
<body>
<div id="root">
<Counter v-bind:init-value="10"/>
</div>
</body>
import Vue from "vue";
Vue.component("Counter",{
// props:{
// initValue:[String,Number]
// },
props:{
initValue:{
type:Number,
required:true
}
},
data:function(){
return {
count:this.initValue
}
},
template:'<div>\
<span>{{count}}</span>\
</div>',
})
const vm = new Vue({
el:"#root"
})
props的类型还可以是一个构造函数。
<body>
<div id="root">
<book-set/>
</div>
</body>
import Vue from "vue";
function Person(firstname,lastname){
this.firstname = firstname;
this.lastname = lastname;
}
Vue.component("Book",{
props:{
author:Person
},
computed:{
fullname:function(){
const {firstname,lastname} = this.author;
return firstname +" "+lastname;
}
},
template:'<div>\
<span>The author of the book is {{fullname}}</span>\
</div>'
})
Vue.component("book-set",{
data:function(){
return {
person:new Person("Steve","Jobs")
}
},
template:'<Book v-bind:author="person"></Book>'
})
const vm = new Vue({
el:"#root"
})
非prop的attribute
- 非props的attributes会被添加到组件根元素上,且可以通过
this.$attrs
访问 - 如果不想将 非props的attributes添加到组件的根元素上,使用
inheritAttrs:false
<body>
<div id="root">
<Counter v-bind:init-value="10"
a="hello"
b="world">
</Counter>
</div>
</body>
import Vue from "vue";
Vue.component("Counter",{
inheritAttrs:false,
props:["initValue"],
template:'<div>\
<span>{{count}}</span>\
</div>',
data:function(){
return {
count:this.initValue
}
},
mounted:function(){
console.log("this.$attrs",this.$attrs);
}
})
const vm = new Vue({
el:"#root"
})
子组件向父组件传递数据
子组件通过调用this.$emit(事件名称)
触发一个事件,父组件则通过v-on:事件名称
监听子组件发出的事件。
本例中,子组件this.$emit("message",this.count)
,$emit()
的第二个参数,父组件里的事件处理程序可通过以下任一方式获取。
- 第一种
<Counter v-on:message='console.log($event)'/>
$event
的值就是this.count
- 第二种
<Counter v-on:message="handleMessage"/>
事件处理程序handleMessage
的第一个参数就是this.count
第一种
<body>
<div id="root">
<Counter v-on:message='console.log($event)'/>
</div>
</body>
import Vue from "vue";
Vue.component("Counter",{
template:'\
<div>\
<span>{{count}}</span>\
<button v-on:click="handleClick">click me</button>\
</div>\
',
data:function(){
return {
count:0
}
},
methods:{
handleClick:function(){
this.count++;
this.$emit("message",this.count);
}
}
})
const vm = new Vue({
el:"#root",
data:{
console:window.console
}
});
第二种
<body>
<div id="root">
<Counter v-on:message="handleMessage"/>
</div>
</body>
import Vue from "vue";
Vue.component("Counter",{
template:'\
<div>\
<span>{{count}}</span>\
<button v-on:click="handleClick">click me</button>\
</div>\
',
data:function(){
return {
count:0
}
},
methods:{
handleClick:function(){
this.count++;
this.$emit("message",this.count);
}
}
})
const vm = new Vue({
el:"#root",
methods:{
handleMessage:function(a){
console.log(a)
}
},
});
自定义事件
自定义事件名最好全小写,如有必要,使用短横线分隔命名(kebab-case
)。
<body>
<div id="root">
<Counter v-on:countInc="handleInc"
v-on:countDec="handleDec"/>
</div>
</body>
import Vue from "vue";
Vue.component("Counter",{
template:'<div>\
<span>{{count}}</span>\
<button v-on:click="onIncrement">+</button>\
<button v-on:click="onDecrement">-</button>\
</div>',
data:function(){
return {
count:10
}
},
methods:{
onIncrement:function(){
this.count++;
this.$emit("countInc");
},
onDecrement:function(){
this.count--;
this.$emit("countDec");
}
}
})
const vm = new Vue({
el:"#root",
methods:{
handleInc:function(){
console.log("handling inc");
},
handleDec:function(){
console.log("handling dec");
}
}
})
针对以上问题,有如下两种解决方法。
内置组件slot实现内容传递
通过slot
往组件中插入内容。
<body>
<div id="root">
<my-component>
<div>world</div>
</my-component>
</div>
</body>
import Vue from "vue";
Vue.component("my-component",{
template:'\
<div>\
<div>hello</div>\
<slot></slot>\
</div>\
'
})
const vm = new Vue({
el:"#root"
})
is属性
像table
标签,其子元素必须是thead
、tbody
、tr
、th
、td
,如果是其他元素,则会被提升到table
的外部。举个例子。
<body>
<div id="root">
<table>
<tr>
<td>hello</td>
<td>world</td>
</tr>
<div>have a nice day</div>
</table>
</div>
</body>
<body>
<div id="root">
<table>
<my-component></my-component>
</table>
</div>
</body>
import Vue from "vue";
Vue.component("my-component",{
template:'\
<div>\
<div>hello</div>\
<div>world</div>\
</div>\
'
});
const vm = new Vue({
el:"#root"
});
table
中的内容被提取到了外面,不符我们的预期,如何破?is
属性很给力!
<body>
<div id="root">
<table>
<tr is="my-component"></tr>
</table>
</div>
</body>
import Vue from "vue";
Vue.component("my-component",{
template:'\
<div>\
<div>hello</div>\
<div>world</div>\
</div>\
'
});
const vm = new Vue({
el:"#root"
});
内置组件component实现动态组件
<component v-bind:is=""></component>
实现动态组件
<body>
<div id="root">
<component v-bind:is="currentId"></component>
<button v-on:click="handleClick">toggle</button>
</div>
</body>
import Vue from "vue";
Vue.component("one-component",{
template:"<div>hello</div>"
});
Vue.component("two-component",{
template:"<div>world</div>"
})
const vm = new Vue({
el:"#root",
data:{
currentId:"one-component"
},
methods:{
handleClick:function(){
if(this.currentId === "one-component"){
this.currentId = "two-component";
}else if(this.currentId === "two-component"){
this.currentId = "one-component";
}
}
}
});
注意,<component></component>
,不要<component/>
。
因为如果component
后有内容,<component/>
会导致其后面的内容无法显示。