Vue | 小记
MVVM
- M Model:数据模型层 (数据)
- V View :视图层 (页面 DOM)
- VM ViewModel:负责监控视图层和数据模型层的数据,并对应的通知另一侧进行修改(监控者)
Vue
Vue 是一套用于构建用户界面的渐进式框架,使用 vue 必须先引入
<div class="box">
<h1>
{{title}}
</h1>
<p>{{age}}</p>
</div>
<script>
var firstVue = new Vue({
el: ".box",
data: {
title: "Vue",
age: 5
}
});
</script>
Vue 的模板语法
v-bind
将 data 中的数据绑定到 html 元素的
属性上
<p class="pic">
<!-- 用v-bind将data中的数据绑定到html元素的属性上 -->
<img v-bind:src="src" v-bind:alt="title" v-bind:width="width" v-bind:title="title">
<!-- 可以简写成以下形式 -->
<img :src="src" :alt="title" :width="width" :title="title">
</p>
<script>
var imgVm = new Vue({
el: ".pic",
data: {
src: "https://cn.vuejs.org/images/logo.png",
title: "Vuelogo",
width: 200
}
});
</script>
v-if
v-if 用于控制 html 元素是否创建
<div id="app">
<ul v-if="list.length"> <!--当满足这个条件的时候,创建ul-->
<li>{{list[0]}}</li>
</ul>
<p v-else> <!--否则,创建p-->
{{title}}
</p>
<p>
<span v-if="salary<6000">当salary小于6000的时候我才创建</span>
<span v-else-if="salary>10000">当salary大于1000的时候我才创建</span>
<span v-else>当上面的都不满足时创建我</span>
</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
list: ["hello"],
title: "v-if的用法",
salary: 10000
}
});
</script>
v-show
v-show 用于控制 html 元素是隐藏还是显示
<div id="app">
<!-- v-show决定html元素是隐藏还是显示 -->
<div v-show="isShow" style="width:300px;height:100px;border:1px solid #ccc"></div>
<input type="button" value="显示/隐藏div" @click="isShow = !isShow"/>
</div>
<script>
// v-if 用于控制html元素是否创建
// v-show 用于控制html元素显示与隐藏
var vm = new Vue({
el:'#app',
data:{
isShow:true,
}
})
v-for
用于遍历数组或者对象
<div id="app">
<ul>
<!-- 遍历数组 item接收的是数组元素 index接收的下标 -->
<li v-for="(item,index) in directives">
<span>{{index+1}}、</span> <span>{{item.name}}</span>
<p>{{item.desc}}</p>
</li>
</ul>
<hr />
<p v-for="(item,index) in directives">
<!-- 遍历普通对象 -->
<span v-for="(val,key) in item">{{keyDescs[key]}}:{{val}}</span>
</p>
<hr />
<p>
<!-- v-for用户控制循环次数 -->
<span v-for="i in count">*</span>
</p>
<hr />
<div>
<!-- template标签可以使它来包裹多个标签,并不影响html的原结构 -->
<template v-for="(item,index) in directives">
<h2>指令名称</h2>
<p>指令描述</p>
<hr />
</template>
</div>
</div>
<script>
// # v-for常用于:
// 遍历数组
// 遍历{}
// 控制循环次数
// template标签
var vm = new Vue({
el: "#app",
data: {
count: 4,
keyDescs: {
id: "ID",
name: "指令名称",
desc: "指令作用"
},
directives: [
{
id: 1,
name: "v-bind",
desc: "绑定html元素的属性"
},
{
id: 2,
name: "v-show",
desc: "控制元素显示或者隐藏"
},
{
id: 3,
name: "v-for",
desc: "遍历数组或者对象"
}
]
}
});
</script>
v-on
vue 中的点击事件
<div id="app">
<h3>现在人数是:{{count}}</h3>
<!-- 绑定单击事件并且调用add方法,此方法写在methods属性中的 事件处理写的是调用函数 -->
<p><input type="button" value="加1" v-on:click="add()"/></p>
<!-- 绑定单击事件并且调用decrement方法,此方法写在methods属性中的 事件处理写的是函数名 -->
<p><input type="button" value="减1" v-on:click="decrement"/></p>
<!-- 绑定单击事件,执行count-=2的js代码 -->
<p><input type="button" value="减2" v-on:click="count-=2"/></p>
<!-- 以缩写方式绑定事件 @事件名="" -->
<p><input type="button" value="加2" @click="count += 2"/></p>
<!-- 直接写方法名时是没有办法再传递其它参数 -->
<select @change="changeKey">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
<!-- $event代表是当前事件对象,Vue中定义的内置变量 -->
<select @change="changeKey2($event,'额外的参数')">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
<p v-if="key">{{styles[key]}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
new Vue({
el: "#app",
data: {
count: 40,
key: "a",
styles: {
a: "red",
b: "blue",
c: "orange"
}
},
// methods属性中定义的都是些方法
methods: {
add() {
// this 代表的是当前vue实例
// this.count 访问到当前实例上的数据中定义的count属性
this.count = this.count + 1;
},
decrement() {
this.count--;
},
changeKey(event) {
// event代表的是当前事件对象,event.target获取事件源,dom元素
console.log(event, event.target, event.target.value);
this.key = event.target.value;
console.log("改变key的值");
},
changeKey2(msg, evn) {
console.log(msg);
this.key = event.target.value;
}
}
});
</script>
v-model
数据双向绑定 v-model
v-model.lazy:延迟视图更新,当按下回车键或者输入框失去焦点,视频是更新。不会随着输入框的内容同步更新。
<div class="box">
<select v-model="imgs" @change="changeImg()">
<option value="./images/1.jpg">1.jpg</option>
<option value="./images/2.jpg">2.jpg</option>
<option value="./images/3.jpg">3.jpg</option>
<option value="./images/4.jpg">4.jpg</option>
<option value="./images/5.jpg">5.jpg</option>
<option value="./images/6.jpg">6.jpg</option>
</select>
<p>
<img :src="src" alt="">
</p>
</div>
<script>
new Vue({
el: ".box",
data: {
imgs: "",
src: ""
},
methods: {
changeImg() {
this.src = this.imgs;
}
}
});
</script>
数据检测更新
动态添加一个对象的属性时并不会触发视图变化(没有被监听)
可以通过 Vue.set(对象,属性名,值)
和 this.$set(对象,属性名,值)
<div id="app">
<div v-for="(item,index) in book">
<p><span>{{index}}:</span>{{item}}</p>
</div>
<select v-model="imgSrc">
<option value="1.jpg">1.jpg</option>
<option value="2.jpg">2.jpg</option>
<option value="3.jpg">3.jpg</option>
</select>
<input type="button" value="设置书的封面图" @click="setImg" />
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
imgSrc: "1.jpg",
book: {
id: 1,
name: "终身成长"
}
},
methods: {
setImg() {
// Vue.set(this.book,'src',this.imgSrc) //动态添加某对象的属性
this.$set(this.book, "src", this.imgSrc); //动态添加某对象的属性
}
}
});
</script>
PS: // 数组的方法 // 能改变原数组的方法:
push(),unshift(),shift(),pop(),splice(),reverse(),sort() ----直接触发视图更新 //
不能改变原数组的方法返回的一个的数组 :concat(),filter(),slice(),map()
----需要将返回的新数组重新赋给原数组 // 通过数组名[下标] = newValue
这样的方式是不能导致视图更新 如:
<div id="app">
<ul>
<li v-for="item in funcs">{{item}}</li>
</ul>
<input type="button" value="修改第二个元素" @click="modify()"/>
</div>
<script>
new Vue({
el:'#app',
data:{
funcs:['slice','chartAt','match','split']
},
methods:{
modify(){
// 将数组的第二个元素改变为some
#错误 this.funcs[1] = 'some'
// 解决方案:
//第一种: Vue.set(this.funcs,1,'some')
//第二种:this.$set(this.funcs,1,'some')
//第三种:this.funcs.splice(1,1,'some')
}
}
})
</script>
事件修饰符
事件修饰符可以连写
- .stop 阻止事件冒泡
<div @click="console.log('div')">
<input type="button" value="点击" @click.stop="console.log('button')"/>
</div>
- .prevent 阻止默认事件
<a href="" @click.prevent="show">查看头相</a>
#连写
<a href="" @click.prevent.stop="show">查看头相</a>
- .once 事件绑定一次
<a href="javascript:;" @click.once.stop="show">只能查看一次头相</a>
计算属性
计算属性放在
computed里面计算属性名是一个方法,该方法必须要返回一个值
<box id="app">
<p>{{sum}}</p>
<input type="text" v-model.number='x'>
<!-- .number 将输入的内容强制转换为数值型 转换的规则是用parseInt()
如果通过它转换结果为NaN则不转换 -->
<input type="text" v-model.number='y'>
</box>
<script>
new Vue({
el: "#app",
data: {
x: "",
y: ""
},
computed: {
sum() {
return this.x + this.y;
}
}
});
</script>
watch
监听
组件 component
在使用组件时,多个单词的名字,要用横线链接。
必须先写组件,再创建 Vue 实例
template 里面写的是 html 模板,它只能包含一个 html 根元素
在组建中的 data 必须是一个函数,并且返回一个对象,这样设计的模式是保证每一个组件之间都是各自独立的,互不影响的。
props 属性值是不能被修改的(单向数据流)
创建全局组件
Vue.component('组件名',组件的配置对象)
1.创建
Vue.component('ThisIsHeader',{
props:[], template:`
<header>
<img src=""/>
<h2>
全局组件适用于整个Vue实例
</h2>
</header>
` })
2.使用
<thisis-header></thisis-header>
属性验证
props:{
属性名:{
type:String, //type(String,Number,Boolean,Object,Array,Function,Promise)
default:()=>''
}
}
通信
父传子
- 父组件
<template id="parent">
<div>
<child :message="msg"></child>
</div>
</template>
- 子组件
在子组件的props中可以拿到父组件传递过来的值
数据写法:
props:['message']
对象写法:
props:{
message:{
type:Object
}
}
子传父
实现方法是父组件自定义一个事件,在子组件中$emit('父组件中自定义的事件名',需要传递的值)
+ 子组件
<div>
<!-- 定义一个传值的方法 -->
<input type="button" value="点击传递" @click="send">
</div>
<script>
export default {
data () {
return {
childValue: '子组件的数据'
}
},
methods: {
send () {
// childByValue是在父组件on监听的方法
// 第二个参数this.childValue是需要传的值
this.$emit('parentEvent', this.childValue)
}
}
}
</script>
+ 父组件
<template>
<child @parentEvent="childByValue"></child>
</template>
<script>
import child from './child'
export default {
components: {
child
},
data () {
return {
name: ''
}
},
methods: {
childByValue:(data)=>{
// childValue就是子组件传过来的值
this.name = data
}
}
}
</script>
非父子组件通信
主要实现方法是定义一个公共vue实例文件bus.js作为中间媒介进行传值
//bus.js
import Vue from 'vue'
export default new Vue()
/*
* 在通信的两个组件中都需引入bus.js
*/
传递:
Bus.$emit('val',this.res)
接受:
Bus.on('val',data=>{
cosole.log(data)
})
Vue实例生命周期
销毁组件或者Vue实例
# 绑定事件,然后在事件上调用clear方法
methods:{
clear(){
this.$destroy(true) //手动销毁vue
}
},
8个事件钩子
- beforeCreate( )
- created( ) -----能拿到实例中的data数据,在这一步即可进行ajxa请求数据
- beforeMount( ) ----可以拿到元素,但是元素里面的数据未被解析渲染
- mounted( )----操作DOM元素的地方,echarts应用在Vue中,Swiper
- beforeUpdate( )
- updated( )
- beforeDestroy( )
- destroyed( )
Vue组件的生命周期
- beforeCreate( )
- created( ) -----可以拿到props属性的值
- beforeMount( ) ----在组件中此方法拿不到DOM元素
- mounted( )----与Vue实例的生命周期不同,组建中需要到这一步才能拿到Dom元素
- beforeUpdate( )
- updated( )-----不建议在update中修改data的数据,会导致页面又重新渲染。可以选择在beforeUpdate或者watch中修改
- beforeDestroy( )
- destroyed( )
axios
axios是一个ajax请求的框架
get方式:axios.get( url ).then( ).catch( )
post方式:axios.post( url , data ).then( ).catch( )
- HTML
<div id="app">
<p><input type="text" v-model="title" /></p>
<p><input type="text" v-model="hours" /></p>
<input type="button" value="添加" @click="add()"/>
<ul>
<li v-for="(item,index) in list">
<span>{{item.title}}</span><span>{{item.hours}}</span>
</li>
</ul>
</div>
- JavaScript
<script>
new Vue({
el: '#app',
data: {
list: [],
title: '',
hours: ''
},
methods: {
getData() {
axios.get('/task/list').then(res => {
if(res.data.error==0){
console.log(res);
this.list = res.data.data.list
}
})
},
add() {
axios({
url: "/task/add",
method: 'post',
data: {
title: this.title,
hours: this.hours
}
}).then(res => {
this.getData()
}).catch(err => {
})
}
},
created() {
this.getData()
}
})
</script>
- axios统一处理请求或响应
统一集中处理接口传入相同参数的设置以及统计处理错误码的设置
// 设置请求的根路径
axios.defaults.baseURL = 'http://localhost:9000/';
// 设置ajax请求超时时间
axios.defaults.timeout = 2000
// 设置请求拦截器
axios.interceptors.request.use((config)=>{
// 在此可以设置所有接口都需要用到的参数
// console.log('interceptors.request',config)
config.headers['usertoken'] = '299429942428423191031031';
let joinStr = config.url.indexOf('?') === -1 ? '?' : '&';
config.url += joinStr + 'v1=1.0'
return config
})
// 设置响应拦截器
axios.interceptors.response.use(result => {
console.log('请求成功时得到的数据',result)
return result.data
},error=>{
if(error.message.includes('timeout')){
alert('请求超时')
return Promise.reject('timeout');
}
return Promise.reject(error);
});
ref属性
$refs--通过它可以获取到html元素或者组件,
this.$refs.html标签的ref属性值 ---获取到对应的dom元素
循环出来的元素上加了ref属性时,通过this.$refs.ref值是可以全部获取的(获取出来的是个数组)
过滤器
- 全局自定义过滤器
# 定义
Vue.filter('过滤器名称',(value)=>{
})
# 使用
<P>{{name|过滤器名称}}</P>
- 局部过滤器
// 在组件的选项中定义
filters: {
capitalize: function (value) { //过滤器名:capitalize
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
自定义指令
除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令,有时候需要对普通DOM元素进行底层操作,这时候就会用到自定义指令。
- 注册全局指令
Vue.directive('指令名',{
//内置三个钩子函数bind,inserted,update
bind(el,binding,vnode){
},
inserted(el,binding,vnode){
},
update(el,binding,vnode){
}
})
- 注册局部指令
// 在组件的选项中定义
directives: {
focus: { //指令名:focus
inserted: function (el,binding,vnode) {
// 指令所绑定的元素,可以用来直接操作 DOM 。
// binding 一个对象,所包含属性参考官网,其中有一个value属性获取到使用指令时传递的参数
el.focus()
}
}
}
// 在模板中使用方式:<input v-focus />
Vue-cli
- 全局安装vue-cli
npm install @vue/cli -g
- 新建一个项目
# 在存放项目的地方打开终端
1.vue create ProjeceName(项目名称)
2.选择Manually select features
3.勾选自己想要的包(Babel,Router,Vuex,css pre-processors,Linter/Formatter)
4.Sass/scss(with node-sass)
5.standard
6.Lint on save
7.ted config files
- 查询npm来源以及更改成淘宝镜像
查询:npm get registry
更改成淘宝镜像: npm set registry="http://registry.npm.taobao.org"
- proxy代理配置
// 在vue.config.js中添加以下代码
devServer:{
port:3000,
'/api':{
target:'http://xxxx.com/',
changeOrigin: true,
pathRewrite: { //路径重写
'/api': ''
}
}
}
动态路由
在定义路由时路径中带有冒号的即是动态路由,例如路径是 /film/:id 为动态路由,通过this.$route.params.id 获取到相应的值。
子路由/嵌套路由
在路由里在嵌套子路由
export default new Router({
routes: [{
path:'/', //重定向:当访问的是根目录的时候,跳转到/films路径
redirect:'/films'
},{
path: '/films',
name: 'home',
component: films,
children:[{ //子路由
path:'',
redirect:'/films/nowplaying' //重定向:当访问/films时,直接跳到/films/nowplaying
},{
path:'nowPlaying',
component:NowPlaying
},{
path:'comingSoon',
component:ComeSoon
}]
},{
path:'/center',
component:Center
}]
})
编程式导航
this.$router.push('路径' )
this.$router.push({path:'路径',query:{参数名:值}})
this.$router.push({name:'路由名称',params:{参数名:值}})
// 注意:this.$router.push方法的参数是对象时,path不能与params搭配使用
// 使用params带参数,需要对应使用name来指明路径,query带参数,则path,name都可以
// 获取动态路由中的参数使用this.$route.params
// 获取路径中?部分的参数使用this.$route.query
# 例如
// /article/11 ===> this.$route.params.xx
// /article/detail?id=11====>this.$router.query.id
路由守卫
- 路由全局守卫 (全局前置守卫(全局拦截))
const router = new VueRouter({ ... })
router.beforeEach((to,from,next)=>{
//to 是即将转向的路由对象
//from 是从哪儿来的路由对象
//next是一个函数,继续执行,即验证通过next,否则拦截
})
- 单个路由守卫 (单个拦截)
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter((to,from,next)=>{
//to 是即将转向的路由对象
//from 是从哪儿来的路由对象
//next是一个函数,继续执行,即验证通过next,否则拦截
})
}
]
})
- 组件内部的路由守卫 (单个拦截)
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
next(vm => {
// 通过 `vm` 访问组件实例
})
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
监听路由
监听路由的变化,当路由发生变化时要执行的操作
watch:{
'$router':{
handler(newVal){
//重新请求调用 当前路由发生变化时将调用该方法,如/user/1切换到/user/2时
},
deep:true,
immediate:true //页面初始化完成立即监听
}
}
路由懒加载
只有访问该组件对应的路径时,才将相对应的js文件打包。按需加载
export default new Router({
routes: [
{
path:'/active',
component: () => import('./views/active.vue')
},
{
path:'/center',
component: () => import('./views/center.vue')
}
]
})
Vuex
各组件之间的数据共享(改变共享数据是需要符合预设的规则才能被修改)
在一个组件中导出了多个方法,在导入的时候要加花括号
- state
mapState是一个方法可以简便获取store中的state
mapGetters是一个方法可以简便获取store中的getters
import {mapState,mapGetters} from 'vuex' //用此方法在组件中渲染的时候则用{{xxx}},否则用{{$store.state.xxx}}
export default {
name: 'home',
computed:{
...mapState(['total','username']),
...mapGetters(['totalMsg'])
},
components: {
}
}
var a = [10,20]
var b = [...a,33,44]
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 状态管理中心
let store = new Vuex.Store({
// 数据中心
state:{
total:10, // 水的库存(桶)
username:'admin'
},
// 同步行为(只有mutation中的方法才能修改state中的数据)
mutations:{
addTotal(state,val){
// state接收的是当前状态管理中心的数据中心
console.log('同步行为addTotal',state,val)
state.total += val
},
reduceTotal(state,val){
console.log('同步行为reduceTotal',state,val)
state.total -= val
}
},
// 异步行为(异步行为是不能直接修改state,只能提交给mutaions中的方法来执行)
actions:{
ybAddTotal(store,val){
// store变量代表是当前状态管理中心
console.log('异步行为ybAddTotal',store,val)
setTimeout(()=>{
// 成功了则提交mutations中的方法
store.commit('addTotal',val)
},3000)
},
ybJianTotal(store,val){
setTimeout(()=>{
// 成功了则提交mutations中的方法
store.commit('reduceTotal',val)
},2000)
}
},
// 一些变量,变量名作为方法,方法一定要返回一个值,根据现有state中某个变量得来的结果
getters:{
totalMsg(state){
// state接收的是当前状态管理中的state
return state.total > 20 ? '' : '库存不足'
}
}
})
export default store

浙公网安备 33010602011771号