简介
vue: 一套用于构建用户界面的渐进式框架。
如何渐进
可理解为分别对应:
- 非常简单的页面
- 比较大点的页面
- SPA(单页面应用)
- 数据管理 vuex
- 脚手架 vue-cli
单页应用
页面跳转: 通过 js 渲染。单页 -> 只有一个HTML 文件
- 特点
- 页面切换快
- 页面中的交互是不刷新页面的
- 加载过的公共资源,无需重复加载(除了首页之外的页面,都通过异步组件方式注册)
- 首屏时间稍慢,SEO差
多页应用
页面跳转:返回新的HTML。多个HTML文件
- 特点
- 首屏时间快,SEO效果好
- 页面切换慢 注:搜索引擎只认识 HTML 中内容,不认识 js 内容,单页应用是靠 js 渲染生成,所以 SEO 排名差
特点
- 易用:会HTML,css,js即可上手。
- 灵活:不断繁荣的生态系统,可以在一个库和一套完整框架之间自如伸缩。
- 高效:20kb min+gzip运行大小,超快虚拟DOM,最省心的优化。
简单使用
// 引入cdn
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<div id="app">
{{ a }}
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
a: 10,
}
})
</script>
语法
v-bind 和 v-on
v-bind绑定属性
v-bind == :
// 完整语法
<a v-bind:href="url">...</a>
// 缩写
<a :href="url">...</a>
v-on事件监听
v-on == @
// 完整语法
<a v-on:click="doSomething">...</a>
// 缩写
<a @click="doSomething">...</a>
// 注意
// clickBtn 默认有 e;clickBtn($event) 则需要传入才有
<button @click='clickBtn($event)'>点击</button>
修饰符
- 事件修饰符:
prevent阻止默认事件stop阻止冒泡self只有在e.target = e.currentTaget的时候才会执行(触发事件元素 和 绑定事件元素 相等的时候)once只执行一次capture事件从冒泡规则 变为 捕获规则
<form action="abc" @click.prevent>
<input type="submit">
</form>
- 按键修饰符
// enter tab esc...
<input type="text" @keydown.ctrl="keyDown">
- 系统修饰符
// ctrl shift alt meta
- 鼠标修饰符
// left right middles
<button @click.right="clickMouse">鼠标</button>
绑定class、style
- class 绑定
:class="{'font-red': isRed}";
:class="[isRedLi]";
:class='[{redClass: true},isRedLi]';
isRedLi: 'font-blue'
- style 绑定
:style="styleObj";
:style="[styleObj,{fontSize: '30px'}]";
styleObj: {
color: 'red',
fontSize: '20px'
}
v-if 和 v-show
-
条件渲染:控制元素的显示与隐藏 注:如果不想要 div,
v-if可以用 template;v-show则不能使用 -
区别
v-if控制的 dom 的移除/添加。v-show控制 dom 的样式显示/隐藏( display )- 频换的切换显示/隐藏 使用
v-show;只判断一次时,使用v-if
v-for
- 列表渲染:基于一个数组来渲染一个列表
- 数组以下 7 种方法可改变数据
// vm.list.push()
push pop shift unshift splice sort reverse
// 也可通过
Vue.set(vm.list,1,{id: '004',title: 'hhh'}) == vm.$set(vm.list,1,{id: '004',title: 'hhh'})
- 对象改变引用可改变数据
vm.obj = {}
// 也可通过
Vue.set(vm.obj,'sex','man') == vm.$set(vm.obj,'sex','man')
key 类型:number、string;唯一的;作用:可提高页面性能
arr: (item,index) in arr
<ul>
<li v-for="item in arr">{{ item }}</li>
// index 数组的索引
<li v-for="(item,index) in arr">{{ item }} -- {{ index }}</li>
</ul>
obj: (value,key,index) in obj
<ul>
<li v-for="value in obj">{{ value }}</li>
// value 属性名,key 属性值,index 索引
<li v-for="(value,key,index) in obj">{{ value }} -- {{ key }} -- {{ index }}</li>
</ul>
num: num in number
<p v-for="item in 10">{{ item }}</p>
str: str in string
<p v-for="str in 'fuj'">{{ str }}</p>
v-model
- 双向数据绑定:数据驱动视图,视图驱动数据
注:
v-model实际上就是:value + @input的语法糖。
// 视图驱动数据:改变视图中input的数据,使得p中的数据随之改变
<div id="app">
<input type="text" :value="value" @input='handleInput' />
<p>{{ value }}</p>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
value: 'fj', // 数据驱动视图:数据在视图中显示
},
methods: {
handleInput(e) {
this.value = e.target.value;
}
}
})
</script>
// checked: true/false
<input type="checkbox" v-model='checked' />
// checkList: [html,css]
<input type="checkbox" value="html" v-model="checkList" />
<input type="checkbox" value="css" v-model="checkList" />
// radioStr: 'html'/'css'
<input type="radio" value='html' v-model='radioStr' />
<input type="radio" value='css' v-model='radioStr' />
// selected: 'html'/'css'
<select v-model="selected">
<option value="html">html</option>
<option value="css">css</option>
</select>
methods、computed、watch
vue中数据查找顺序:data、methods、computed
- 方法
methods:写一些逻辑的时候,触发一个事件的时候;函数封装的时候 - 计算属性
computed: 要得到一个新的数据的时候 - 侦听器
watch:当在某个数据改变要做某个事情的时候
<div id="app">
<p>{{ fullName }}</p>
<p>{{ age }}</p>
<p>{{ fullNameFn() }}</p>
</div>
<script>
// methods 和 computed
// 控制台输入 vm.age = 18 打印一次 methods,说明方法没有缓存,不如计算属性
let vm = new Vue({
el: '#app',
data: {
firstName: 'fu',
lastName: 'jie',
age: 28,
// fullName: 'fu jie', // watch 时使用
},
// 不想在插值表达式中进行计算,只是展示就好
// 计算属性重要的点:内置缓存
// computed: {
// fullName() {
// console.log('--- computed ---')
// return this.firstName + ' ' + this.lastName;
// }
// },
// vm.fullName = 'han xiao'
computed: {
fullName: {
get() {
console.log('--- computed get ---')
return this.firstName + ' ' + this.lastName;
},
set(val) {
console.log('--- computed set ---: ',val);
this.firstName = val;
this.lastName = '';
}
}
},
// methods: {
// fullNameFn() {
// console.log('--- methods ---')
// return this.firstName + ' ' + this.lastName;
// }
// },
// 监听 firstName 和 lastName
// watch: {
// firstName() {
// console.log('--- watch firstName ---')
// this.fullName = this.firstName + ' ' + this.lastName;
// },
// lastName() {
// console.log('--- watch lastName ---')
// this.fullName = this.firstName + ' ' + this.lastName;
// }
// }
})
</script>
生命周期
- 生命周期函数就是 vue 实例在某一个时间点会自动执行的函数
beforeCreate:组件实例化之前执行的函数(可写一些加载的效果)created:组件实例化完毕,但页面还未显示(一般在这里面写请求到的数据)监听data中数据。beforeMount:组件挂载前,页面仍未显示,但虚拟DOM已经配置了mounted:组件挂载后,此方法执行后,页面显示。监听页面DOM渲染完毕。beforeUpdate:组件更新前,页面仍未更新,但虚拟DOM已经配置updated:组件更新,此方法执行,页面显示更新后的。监听methods中方法执行后对DOM的改变beforeDestory:组件销毁前destoryed:组件销毁
- 第一次页面加载会触发的钩子:
beforeCreate,created,beforeMount,mounted
- 改变数据触发以下钩子
vm.msg = '123':
beforeUpdate,updated
- 销毁后触发以下钩子
vm.$destroy():
beforeDestory,destoryed
组件
简单组件
<div id="app">
<child-one></child-one>
</div>
// 全局组件
Vue.component('childOne',{
template: `<div>child-one</div>`
})
// 局部组件
let childOne = {
template: `<div>child-one</div>`
};
let vm = new Vue({
el: '#app',
components: {
childOne,
},
})
父子之间组件传值
<div id="app">
<ul>
<li-item v-for="(li,index) of list" :to-do="li" :index="index" @add="clickAdd"></li-item>
</ul>
</div>
<script>
let LiItem = {
props: {
toDo: Object,
index: Number,
},
template: `
<li>
<span>{{ toDo.name }}</span>
<span @click="clickLiAdd(toDo,index)">{{ toDo.age }}</span>
</li>
`,
methods: {
clickLiAdd(todo,index) {
this.$emit('add',todo,index)
}
}
};
let vm = new Vue({
el: "#app",
components: {
LiItem,
},
data: {
list: [
{name: 'fj',age: 28},
{name: 'yjx',age: 27},
{name: 'hx',age: 24},
]
},
methods: {
clickAdd(item,index) {
this.list.splice(index,1,{name: item.name,age: ++item.age})
}
}
})
</script>
- 注意:
- 父组件向子组件传递了一个属性,子组件通过props属性接收了,那么就称作props,如果没有接收,就称作非props。
- 子组件中 data 必须是个函数。
- vue 中存在单向数据流概念,所以父组件数据传到子组件后要在 data 中克隆一份。
// H5中规定 table>tbody>tr,所以tbody中不能直接使用组件 <row>,可以用 is="row"
<table>
<tbody>
<tr is="row"></tr>
</tbody>
</table>
// 加 : 传入的是数字,不加 : 传入的是字符串,因为 加引号后面是 js表达式
<child :cont='1'></child>
// 加 native 可直接操作组件,因为触发的不是自定义事件,而是原生事件
<child @click.native="clickChild"></child>
- props 写法
props: ['cont',...],
props: {
toDo: Object,
index: Number,
},
props: {
cont: [String,Number], // 组件中参数校验
},
props: {
cont: {
type: Number, // 组件中参数校验
required: true, // 必传
default: '默认值',
validator(val) {
// return val.length > 5; // 更多的校验
}
}
}
非父子间传值
- 通过 vuex 方法传值
- Bus/总线/发布订阅模式/观察者模式
Vue.prototype.bus = new Vue()
// 子组件点击事件中
clickChild() {
this.bus.$emit('change',this.selfContent)
}
// 子组件 mounted 钩子函数中
let that = this;
this.bus.$on('change',function(msg){
console.log(msg)
that.selfContent = msg
})
动态组件
例如 多个组件之间动态切换
// 方法一
<component :is="type"></component>
// 方法二
<child-one v-if="type == 'child-one'"></child-one>
<child-two v-if="type == 'child-two'"></child-two>
type: 'child-one' // child-two
插槽
使用情景:当子组件中有一部分内容是根据父组件传递过来的DOM进行显示的时候。
匿名插槽
<child>
<p>开心吗</p>
</child>
Vue.component('child',{
template: `
<div>
<slot>默认内容</slot>
</div>
`
})
具名插槽
<child>
// 注意:v-slot 写法只能和 template结合,可显示默认内容
<template class="header" v-slot:header></template>
// 注意:slot 写法,不显示默认内容
<div class="footer" slot="footer"></div>
</child>
Vue.component('child',{
template: `
<div>
<slot name="header">
<h1>default header</h1>
</slot>
<div class="content">content</div>
<slot name="footer"></slot>
</div>
`
})
插槽作用域
当子组件循环,或者某一部分为外部传进来的时候
<child>
// 传入插槽, 一般 template 开头结尾的
<template slot-scope="item">
<h2>{{ item.li }} - hello</h2>
</template>
</child>
Vue.component('child',{
data() {
return {
list: [1,2,3,4]
}
},
template: `
<div>
<ul>
<slot v-for="li of list" :li="li"></slot>
</ul>
</div>
`
})
请求数据 axios
// 安装 axios
cnpm i axios --save-dev
- 局部使用
// 所用页面
import axios from 'axios'
axios.get(url,{}).then(res=>{
// ...
})
- 全局使用
// main.js
import axios from 'axios'
Vue.prototype.$axios = axios
axios.defaults.baseURL = '...com'
...
// 页面中
this.$axios.posturl,{}).then(res=>{
// ...
})
脚手架 vue-cli
脚手架:自动的构建一个项目、使用 ES6 语法、打包和压缩 js 为一个文件、项目文件在环境中编译而不是浏览器...
// 查看 node 版本
node -v (node > 8.9)
// 如果之前已经安装过旧版本(非3.X)脚手架,需要先卸载旧版本
npm uninstall vue-cli -g
// 安装脚手架,用于生成项目
npm install -g @vue/cli
// 快速原型开发,编译.vue文件
npm install -g @vue/cli-service-global
// 运行 vue文件
vue serve App.vue(文件名)
// 查看 vue-cli 版本
vue -V
// 如果仍然需要使用旧版本的 vue init 功能,可以全局安装一个桥接工具
npm install -g @vue/cli-init
// 利用脚手架搭建项目
vue create vue-app
// 运行
npm run dev
// 打包
npm run build
// vsCode 安装插件名字:Vetur
安装时一些插件
- babel: ES6转为ES5;
- eslint: 规范代码风格;
- TypeScript: 简称TS,写一些大型项目或者框架的时候用;
- pwa: 初始进入网页耗费流量,再进就不耗费了,相当于缓存;
- Router: 切换页面;
- Vuex: 管理数据、状态的时候用到;
- CSS Pre-..: css预处理语言:sass、less...;
目录结构
-
node_modules: 安装的包
-
public: 相当于根目录,可以放一些图片、json(绝对路径,不会经过webpack处理) (接口未提供时,可以在这里面造些假数据,直接通过根目录就能访问)
-
.gitignore: 里面放不用上传到git的一些文件;
-
babel.config: 配置babel的;
-
package.json: 项目的信息;
-
package-lock.json: 依赖包文件里面包的一些信息;
-
README: 对项目的一些描述;
-
src
- assets: 放置一些资源,图片、css(相对路径,经过webpack处理)
- components: 放置一些组件
- App.vue: 主入口的一个组件
- main.js: 全局配置
经过 webpack 处理后会生成哈希类型,不会被缓存,会实时更新。
// main.js
import Vue from 'vue'; // 表示从包里面引入;
import App from './App.vue'; // 表示从页面引入;
Vue.config.productionTip = false; // 生产环境下没有命令行的警告;
new Vue({
render: h=>h(App), // 虚拟对象转换为真实对象
}).$mount('#app') // 函数实例化,并挂载在#app上
// 打包时根目录下新增:vue.config.js
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? './'
: '/'
}
// 在vue-cli.3.3版本后 baseUrl被废除了,因此这边要写成 publicPath。
其他
v-
v-text
<div v-text="text"></div>
text: 'hello world'
v-html
<div v-html="html"></div>
html: `<span style="color:red;">html</span>`
v-once
只渲染一次
<div id="app" v-once></div>
ref
- ref:DOM对象的引用是该对象,组件的引用是组件的实例对象;
- ref 同名时,后添加的会覆盖前面的,引用指向的最后一个元素;
- 在 v-for 时添加引用,引用的值类型时数组,数组里面一个个的dom对象/组件实例对象。
<div ref='box'>123</div>
console.log(this.$refs.box.innerHTML); // 123
$nextTick
vm.a = 20;
console.log(vm.a); // 20
// 更改数据是同步的,但是更改过后要去视图更新的操作是异步的。
// 此时页面也是20,但是下面打印10
console.log(vm.$el.innerHTML); // 10
// 等待dom执行后再执行。
vm.$nextTick
vm.$nextTick(()=>{
console.log(vm.$el.innerHTML); // 20
})
vm.$mount('#app'); == el: '#app';
框架和库的区别
- 库:将代码集合成一个 产品,供开发者去使用,开发者去调用库中的方法去实现自己的功能。(控制权在自己手里,想用库就用不想用就用原生)
例子:jQuery、zepto... - 框架:为了解决一类问题而开发出来的产品,基于自身的特点向用户提供一套完整的解决方案。(控制权在框架手里,里面的规则方法要遵守)
例子:vue、react...