目录
Vue.js 入门和使用实践
一、软件版本
- Vue版本:2.x
- 官网:https://cn.vuejs.org
- 编程工具:HBuilder X
二、文档指引
- 基础知识 -> 案例实践 -> TodoList -> Vue-cli -> Todolist
三、Vue基础语法
1.创建第一个Vue实例
HBuilder X 菜单 -> 视图 -> 显示项目管理器 -> 将 Vue 文件夹拖进来
- 进入官网 -> 学习 -> 教程 -> 安装 -> 直接引用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<div id="root">{{msg}}</div>
<script>
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
data:{ // vue实例的数据
msg: "Hello World" // 定义msg属性,并赋值 (提供el绑定的节点使用)
}
});
</script>
</body>
</html>
2.挂载点、模板与实例
- 挂载点、模板和实例之间的关系
- 实例和挂载点的关系
- Vue 实例通过 el 属性绑定挂载点
- id=“root” 的 div 是 Vue 实例的挂载点
- Vue 实例只处理挂载点下的数据绑定
- 挂载点和模板的关系
- 挂载点下的内容称为模板或模板内容
- 模板内容可以在初始化 Vue 实例时定义 template 属性
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<!-- 当前 div 称为 Vue 实例的挂载点 -->
<div id="root">
<!-- 模板内容 -->
<h1>h1-msg = {{msg}}</h1>
</div>
<!-- 当前 msg 并不在 Vue 实例的挂载点下,实例无法处理 msg 数据绑定 -->
<div>{{msg}}</div>
<script>
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
template: '<h2>h2-msg = {{msg}}</h2>', // 模板内容
data:{ // vue实例的数据
msg: "Hello World" // 定义msg变量,并赋值 (提供el绑定的节点使用)
}
});
</script>
</body>
</html>
3.Vue实例中的数据、事件和方法
- {{ }} 称为 插值表达式
- v-text 指令(适合纯文本)
- v-html 指令(适合需要添加 html 标签)
- 模板指令 v-on: 表示事件绑定, click 表示绑定点击事件,执行的方法,需要在 Vue 实例中定义 methods 属性(v-on 可以简写为 @ ,例如:
@click
表示点击事件) - 更改数据的显示时,并不需要操作 dom,而是修改变量值,变量值修改后,Vue 会自动更新页面数据(由面向 dom 编程,变成面向数据编程)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<!-- 当前 div 称为 Vue 实例的挂载点 -->
<div id="root">
<!-- 模板内容 -->
<!-- {{}} 称为 插值表达式 -->
<h1>h1-num = {{num}}</h1>
<!-- 通过 v-text 绑定变量 -->
<h2 v-text="msg"></h2>
<!-- 通过 v-html 绑定变量 -->
<h3 v-html="content"></h3>
<!-- 模板事件:div 绑定点击事件, Hello -> World -->
<div @click="handleClick">{{hello}}</div>
</div>
<script>
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
data:{ // vue实例的数据
msg: "Hello World" ,// 定义msg变量,并赋值 (提供el绑定的节点使用)
num: 123456,
content: '<span style="color:red">Hello World</span>',
hello: "Hello"
},
methods:{// 事件方法
handleClick: function(){
// 修改 hello 变量的值
this.hello = 'World'
}
}
});
</script>
</body>
</html>
4.Vue中的属性绑定和双向数据绑定
- 模板指令 v-bind: 表示属性绑定, title 是 div 标签的属性名,绑定的数据,在 Vue 实例的 data 属性中设置(v-bind 可以简写为 : ,例如:
:title
表示绑定 title 属性) - 单项绑定:Vue 实例对象中的数据决定了页面显示的内容,而页面无法修改数据
- 模板指令 v-model 表示标签属性值和 Vue 实例中 data 属性的变量进行双向绑定,当 input 标签内容发生变化,相应绑定的 Vue 实例中 data 属性的变量也会发送变化
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<dir :title="title">Hello World</dir>
<!-- 当使用模板指令后,等号之后的字符串就是 js 的表达式 -->
<dir :title="'title: ' + title">Hello World</dir>
<!-- 双向绑定 -->
<input v-model="content" />
<!-- 单项绑定 -->
<div>{{content}}</div>
</div>
<script>
//创建 Vue 实例
new Vue({
el: "#root",
data:{ // vue实例的数据
title: "this is Hello World",
content: "this is content"
}
});
</script>
</body>
</html>
5.Vue中的计算属性和监听器
- Vue 实例中定义 computed 属性,在 computed 属性中,实现计算属性的方法
-
- (计算属性是通过其它属性计算出来的值)
- (只有当计算属性的方法中的属性发生变化的时候才会重新计算,否则使用上一次计算的缓存)
- Vue 实例中定义 watch 属性,在 watch 属性中,定义需要监听的属性和执行方法的内容(用于监听数据或计算属性的变化,当发生变化时,执行对应的方法)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
姓:<input v-model="firstName" />
名:<input v-model="lastName" />
<div>{{fullName}}</div>
<div>{{count}}</div>
</div>
<script>
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
data:{
firstName: "",
lastName: "",
count: 0
},
computed:{ //计算属性
fullName: function(){
return this.firstName + ' ' + this.lastName;
}
},
watch:{ //监听器
/* firstName: function(){
this.count ++;
},
lastName: function(){
this.count ++;
} */
fullName: function(){
this.count ++;
}
}
});
</script>
</body>
</html>
6.v-if、v-show与v-for指令
- v-if 指令表示是否从 dom 节点中删除指定的标签
- v-show 指令表示是否通过 css 的 display 来隐藏标签
- v-for 指令表示循环列表数据(可通过定义 :key 提升 for 循环的性能,key值不能相同;如果需要频繁的对列表进行变更,key值会有所讲究)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<div v-if="show">hello world</div>
<div v-show="show">Hello World</div>
<button @click="handleClick">toggle</button>
<ul>
<!-- list 每次循环时,将数据保存到item中, index 保存循环的下标 -->
<li v-for="(item, index) of list" :key="index">{{item}}</li>
</ul>
</div>
<script>
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
data:{ // vue实例的数据
show: true,
list: [1, 2, 3]
},
methods:{ // 事件方法
handleClick: function(){
this.show = !this.show;
}
}
});
</script>
</body>
</html>
四、Vue中的组件
1.Todolist 功能开发
- input 标签和 Vue 实例的数据变量做双向绑定(v-model),ul 标签中循环一个列表(v-for),【提交】按钮绑定点击事件(@click),事件方法中,向 list 中 push input 标签填写的内容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<input v-model="inputVal" />
<button @click="handleClick">提交</button>
<ul>
<li v-for="item of list">{{item}}</li>
</ul>
</div>
<script>
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
data:{ // vue实例的数据
inputVal: "",
list: []
},
methods:{ // 事件方法
handleClick: function(){
this.list.push(this.inputVal);
this.inputVal = '';
}
}
});
</script>
</body>
</html>
2.Todolist 组件拆分
- 将一个复杂的页面拆分成多个组件,每个组件独立维护
- 组件之间通信(参数的传递)
定义组件在 Vue 实例之前
- 全局组件定义:Vue.component()
- 局部组件定义:带 template 属性的对象,并且使用 Vue 实例的 components 属性中声明组件
- 通过定义的组件名称,在模板中以 组件名称标签 进行使用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<input v-model="inputVal" />
<button @click="handleClick">提交</button>
<ul>
<!-- 使用组件,定义 content 参数,将v-for中的item传入 -->
<todo-item
v-for="(item, index) of list"
:key="index"
:content="item">
</todo-item>
</ul>
</div>
<script>
// 定义 todo-item 组件(全局组件)
/* Vue.component('todo-item',{
props: ['content'], // 接送传递的参数
template: '<li>{{content}}</li>'
}); */
// 定义局部组件(需要在Vue实例中注册)
var TodoItem = {
props: ['content'],
template: '<li>{{content}}</li>'
}
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
data:{ // vue实例的数据
inputVal: "",
list: []
},
methods:{ // 事件方法
handleClick: function(){
this.list.push(this.inputVal);
this.inputVal = '';
}
},
components:{ // 注册组件
'todo-item': TodoItem
}
});
</script>
</body>
</html>
3.组件与实例的关系
- 一个组件就是一个 Vue 的实例(因为组件中也可以使用 Vue 实例的属性,例如:methods)
- 对于父组件(Vue 实例),如果没有指定 template 属性,它会把挂载点下的内容当成模板使用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<input v-model="inputVal" />
<button @click="handleClick">提交</button>
<ul>
<!-- 使用组件,定义 content 参数,将v-for中的item传入 -->
<todo-item
v-for="(item, index) of list"
:key="index"
:content="item">
</todo-item>
</ul>
</div>
<script>
// 定义 todo-item 组件(全局组件)
/* Vue.component('todo-item',{
props: ['content'], // 接送传递的参数
template: '<li>{{content}}</li>'
}); */
// 定义局部组件(需要在Vue实例中注册)
var TodoItem = {
props: ['content'],
template: '<li @click="handleClick">{{content}}</li>',
methods:{ // 事件方法
handleClick: function(){
alert('click');
}
}
}
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
data:{ // vue实例的数据
inputVal: "",
list: []
},
methods:{ // 事件方法
handleClick: function(){
this.list.push(this.inputVal);
this.inputVal = '';
}
},
components:{ // 注册组件
'todo-item': TodoItem
}
});
</script>
</body>
</html>
4.实现Todolist的删除功能
-
父组件给子组件传值,是通过属性进行传递的
-
子组件将父组件的定义数据进行删除(通过发布订阅模式进行实现;子组件发布事件,父组件自动订阅这个事件)
-
子组件点击 item 时,触发子组件自定义 delete 属性,delete 属性会调用父组件定义的方法,在方法中执行删除数据的操作
-
通过上面描述,自定义的 delete 属性,更像是自定义了一种事件类型,可以调用父组件方法的事件类型
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 入门</title>
<!-- 将 vuejs 的引入放在 head 部分可以防止抖屏的现象 -->
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<input v-model="inputVal" />
<button @click="handleClick">提交</button>
<ul>
<!-- 使用组件,定义 content 参数,将v-for中的item传入 -->
<todo-item
v-for="(item, index) of list"
:key="index"
:content="item"
:index="index"
@delete="handleDel">
</todo-item>
</ul>
</div>
<script>
// 定义 todo-item 组件(全局组件)
/* Vue.component('todo-item',{
props: ['content'], // 接送传递的参数
template: '<li>{{content}}</li>'
}); */
// 定义局部组件(需要在Vue实例中注册)
var TodoItem = {
props: ['content', 'index'],
template: '<li @click="handleClick">{{content}}</li>',
methods:{ // 事件方法
handleClick: function(){
// 通知父组件,触发自定义的 delete 事件,传递 index
this.$emit('delete',this.index)
}
}
}
//创建 Vue 实例
new Vue({
el: "#root", // 指定vue实例接管的 element 元素(绑定id=root的dom节点)
data:{ // vue实例的数据
inputVal: "",
list: []
},
methods:{ // 事件方法
handleClick: function(){
this.list.push(this.inputVal);
this.inputVal = '';
},
handleDel: function(index){
// splice 从 index 开始, 删除一项
this.list.splice(index, 1);
}
},
components:{ // 注册组件
'todo-item': TodoItem
}
});
</script>
</body>
</html>
五、Vue-cli
1.vue-cli的简介与使用
- Vue 脚手架工具 = vue-cli,快速构建标准的 vue 项目目录,自带 webpack 的各种配置
- 安装
安装 Node.js 【https://www.runoob.com/nodejs/nodejs-install-setup.html】
进入官网 -> 学习 -> 教程 -> 安装 -> 命令行工具(CLI)-> Vue CLI 的文档 -> Getting Started
- 使用 npm 创建
$ npm install -g @vue/cli
$ vue create vue-project
$ cd vue-project
$ npm run serve
- 使用 webpack 创建
$ npm install -g @vue/cli-init
$ vue init webpack my-project
$ cd my-project
$ npm run dev
- 最外层的 index.html 是项目入口,其中 为挂载点,Vue 实例创建在 src/main.js 中,模板内容在 src/app.vue 中
- 单文件组件,既一个文件里包含了所有组件内容(.vue 文件,文件包含 template、script、style)
- 项目中自动配置 webpack 的热加载功能(保存页面触发)
- 支持 ES5 和 ES6 代码
2.使用vue-cli开发Todolist
- 当运行 npm run dev 时,npm会加载项目的 package.json 中的 dev 参数
“scripts”: {
“dev”: “webpack-dev-server --inline --progress --config build/webpack.dev.conf.js”,
“start”: “npm run dev”,
“unit”: “jest --config test/unit/jest.conf.js --coverage”,
“test”: “npm run unit”,
“lint”: “eslint --ext .js,.vue src test/unit”,
“build”: “node build/build.js”
}
- 拷贝改名 src/App.vue 为 src/TodoList.vue(data 变成函数)
<template>
<!-- Vue模板内容规定,最外层只能有一个根标签 -->
<div>
<input v-model="inputVal" />
<button @click="handleClick">提交</button>
<ul>
<!-- 订阅 delete 事件 -->
<todo-item
v-for="(item, index) of list"
:key="index"
:content="item"
:index="index"
@delete="handleDel">
</todo-item>
<!-- <li v-for="(item, index) of list" :key="index">{{item}}</li> -->
</ul>
</div>
</template>
<script>
// 导入局部组件
import TodoItem from './components/TodoItem'
export default {
/* data: function(){
return {
inputVal: '',
list: []
}
},
methods: {
handleClick: function(){
this.list.push(this.inputVal);
this.inputVal = "";
}
} */
// ES6 简化语法
data (){ // 数据
return {
inputVal: '',
list: []
}
},
methods: { // 事件方法
handleClick (){
this.list.push(this.inputVal);
this.inputVal = "";
},
handleDel (index){
this.list.splice(index, 1);
}
},
components: { // 组件注册
'todo-item': TodoItem
}
}
</script>
<style>
</style>
- 修改对应 src/main.js 的引用
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import TodoList from './TodoList.vue'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App:TodoList },
template: '<App/>'
})
- 拆分组件(局部组件),拷贝改名 src/components/HelloWorld.vue 为 src/components/TodoItem.vue
<template>
<li @click="handleDel">{{content}}</li>
</template>
<script>
export default {
props: ['content', 'index'],
methods: {
handleDel (){
// 发布 delete 事件,传递参数值
this.$emit('delete', this.index);
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
3.全局样式与局部样式
- 如果组件中 style 添加了 scoped 后,则表示为局部样式,style 的样式仅作用于当前组件
- 否则表示为全局样式,其它有关联的组件也会对该样式生效
- 例如:src/components/TodoItem.vue
<template>
<li class="item" @click="handleDel">{{content}}</li>
</template>
<script>
export default {
props: ['content', 'index'],
methods: {
handleDel (){
// 发布 delete 事件,传递参数值
this.$emit('delete', this.index);
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<!-- 局部样式: 添加 scoped 属性后,style 仅限当前页面可用 -->
<!-- -->
<style scoped>
.item{
color: springgreen;
}
</style>