Vue学习 Vue基础语法 v-bind: 动态绑定属性 可简写为: 可绑定class属性或者style属性 :class或:style或input里面的值:input
v-on: 事件监听 可简写为@ 一般监听点击事件@click
把监听事件用在封装的组件上是不会产生监听效果的,如果我们想要监听一个组件的原生事件时,必须给对应的事件加上.native修饰符,才能进行接听。
v-for 遍历数组
v-show 用于控制元素的渲染
v-model 实现数据的双向绑定
组件通信: 父传子:用props属性来传; 子传父:$emit发射事件来传; 父访问子:$resf对象去访问; 子访问父:直接访问根$root;
父传子-props 值肯定是定义在父组件中的,供所有子组件共享。所以要在父组件的data中定义值,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="../js/vue.js" > </script > </head > <body > <div id ="app" > <bb :c-message ='message' > </bb > </div > <template id ="bb" > <div > <h1 > 我是子组件</h1 > <h2 > {{cMessage}}</h2 > </div > </template > <script > const app = new Vue({ el:'#app' , data:{ message:'hello Vue,与你的第一次相遇' }, components:{ bb:{ template:"#bb" , props:{ cMessage:{ type:String , default :'' , required:true } } } } }) </script > </body > </html >
子传父 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="../js/vue.js" > </script > </head > <body > <div id ="app" > <child-cpn @increment-click ="changeTotal" @decrement-click ="changeTotal" > </child-cpn > <h2 > 点击次数:{{total}}</h2 > </div > <template id ="childCpn" > <div > <button @click ="increment" > +1</button > <button @click ="decrement" > -1</button > </div > </template > <script > const app = new Vue({ el:'#app' , data:{ total:0 }, methods:{ changeTotal(counter){ this .total = counter; } }, components:{ 'child-cpn' :{ template:'#childCpn' , data(){ return { counter:0 } }, methods:{ increment(){ this .counter++; this .$emit('increment-click' ,this .counter); }, decrement(){ this .counter--; this .$emit('decrement-click' ,this .counter); } } } } }) </script > </body > </html >
子组件data必须为函数 根组件除外的组件中的data必须为函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <!DOCTYPE html> <html lang ="en" > <head > <meta charset ="UTF-8" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > <script src ="../js/vue.js" > </script > </head > <body > <div id ="app" > <h2 > {{message}}</h2 > <cpn > </cpn > <cpn > </cpn > <cpn > </cpn > </div > <template id ="cpn" > <div > <h2 > 当前计数:{{counter}}</h2 > <button @click ='increment' > +</button > <button @click ='decreament' > -</button > </div > </template > <script > Vue.component('cpn' ,{ template:'#cpn' , data(){ return { counter:0 } }, methods:{ increment(){ this .counter++; }, decreament(){ this .counter--; } } }) const app = new Vue({ el:'#app' , data:{ message:'Hello Vue' } }) </script > </body > </html >
js高阶函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <script> const nums = [10 ,30 ,50 ,200 ,145 ,60 ,666 ]; let newNums = nums.filter(function (n ) { return n<100 ; }) console .log(newNums); let new2Nums = newNums.map(function (n ) { return n*2 ; }) console .log(new2Nums); let total = new2Nums.reduce(function (preValue,n ) { return preValue+n; },0 ) console .log(total); const nums2 = [10 ,30 ,50 ,200 ,145 ,60 ,669 ] let total2 = nums2.filter(function (n ) { return n<100 ; }).map(function (n ) { return n*2 ; }).reduce(function (preValue,n ) { return preValue+n; },0 ) console .log(total2); </script>
Vue生命周期 脚手架CLI2 初始化项目:vue init webpack 项目名
eslint是用来检测代码规范的,一般选择不用
npm run dev 运行项目
npm run build 打包项目
如果安装额外插件的时候,–save是指运行时依赖,–save -dev是指开发时依赖。
runtime-compiler和runtime-only的区别 ,区别在main.js文件里面,
runtime-compiler编译过程:template->ast(抽象语法树)->rander(函数)->virtual dom->true dom->UI
runtime-only编译过程:rander->virtual dom->UI
相比性能而言,runtime-only性能更高,代码量更少,轻6KB
runtime-only直接使用render函数编译,render函数相当于的参数h相当于createElement函数,
上图的传入对象是App,rander函数省略了template的编译,那么App里面的template到底是如何执行的?其实App转化成了一个对象,而里面的tempalte被转化成了rander函数,是被谁转化的呢,是被package.json文件里面的vue-template-compiler插件转化的。
npm run dev 流程图:
npm run build 流程图:
脚手架CLI3 初始化项目:vue create 项目名
npm run serve 运行项目
npm run build 打包项目
启动界面话配置服务器:vue ui
路由 前端渲染和后端渲染的区别
后端路由:后端处理URL和页面之间的映射关系,当我们页面中需要请求不同的路径内容时,交给服务器来进行处理,服务器渲染好整个页面,并将页面返回给客户端,这种情况下渲染好的页面,不需要单独加载任何的js和css,可以直接交给浏览器展示,这样有利于SEO的优化。
前后端分离:后端只负责提供数据,不负责任何阶段的内容。
前端渲染:浏览器中大量显示的数据,先有ajax请求,调用相应的api接口,然后向数据库拿数据,然后这些数据拿到前端,由js代码执行。当访问一个网站的时候,静态服务器会把所以的html css js 代码都加载出来,然后这个时候,前端路由切换相应的页面,这个时候,就不用再次去请求静态服务器了,路由直接为你去找已经加载好的全部资源,然后再调用相应的组件。特点:改变URL的时候,页面是不进行刷新的,不需要向服务器请求资源。
安装路由:npm install vue-router –save
使用路由第一步:通过Vue.use来安装VueRouter插件;第二步:创建VueRouter对象;第三步导出模块,将router对象传入到Vue实例中。
标签是一个v-router中已经内置的组件,它会被渲染成一个标签,跳转路径使用的是属性to,而属性tag的话可以决定渲染成什么样的组件,比如button。active-class属性当路由匹配成功时,会自动给当前元素设置一个router-link-active的class。
会根据当前的路径,动态渲染出不同的组件.
动态路由 动态路由通过v-bind绑定url和拼接url的方式,来动态获取路由
然后进行动态展示
如果使用默认路由的话,使用redirect来设置。
url的hash和html5的history location.hash = ‘aaa’ ,相当于切换页面,但是不会刷新页面
history.pushState({},’’,’home’) , 此个方法也不会刷新页面,此方法相当于数据结构的栈,push进行入栈操作,最后压入的东西永远在最前面,然后使用history.bcak()的时候,它就会进行出栈,把原来在栈顶的操作移除掉
history.go()能根据指定的参数,进行页面返回前进。
history.forward根据参数进行前进。
history.replaceState({},’’,’home’) 指替换,不能返回之前的url
路由懒加载 当打包构建应用的时候,javascrit包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才能加载对应组件,用到时再加载。
路由参数传递 to属性可以传对象,里面的query属性可传参数
keep alive问题 keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
router-view也是一个组件,如果直接被包在keep-alive里面,所有匹配到的视图组件都会被缓存。
当我们使用生命周期函数去渲染的时候会发现,当我们切换组件的时候,原来所在的组件会被destroy,而点击一个页面的时候会触发created函数。
把标签包到标签中,就可使用keep-alive,就不会被destroyed,可以大大提高性能
keep-alive存在的时候才可使用生命周期函数activated()和deactivated()函数。
使用
可以排除组件。
导航守卫 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' import Detail from '../views/Detail.vue' const User = () => import ( '../views/User.vue' )const DetailNews = () => import ('../views/DetailNews.vue' )const DetailMessage = () => import ('../views/DetailMessage.vue' )const Profile = () => import ('../views/Profile.vue' )const Test = () =>import ('../views/Test.vue' )Vue.use(VueRouter) const routes = [ { path: '/' , name: 'Home' , component: Home, meta:{ title:'首页' } }, { path:'/test' , name:'Test' , component:Test }, { path: '/detail' , name: 'Detail' , component: Detail, meta:{ title:'详情页' }, children:[ { path:'' , redirect:'news' }, { path:'news' , component:DetailNews }, { path:'message' , component:DetailMessage } ] }, { path: '/user/:userId' , name: 'User' , component: User, meta:{ title:'用户页' } }, { path: '/profile' , name: 'Profile' , component: Profile }, { path: '/about' , name: 'About' , meta:{ title:'关于' }, component: () => import ( '../views/About.vue' ) }, ] const router = new VueRouter({ routes, mode:'history' }) router.beforeEach((to,from ,next )=> { document .title = to.meta.title; next() }) export default router
tabbar 创建tabbar:vue create tabbar
Promise promise是异步编程的一种解决方案。一种常见的场景就是网络请求,当我们封装一个网络请求的函数时,因为不能立刻拿到结果,所以不能像简单的3+4+7一样的将结果返回,所以我们往往会传入另外一个函数,在数据请求成功之时,将数据通过传入的函数回调出去。当网络请求简单的时候,倒是没多大的影响,但是当我们的网络请求非常复杂的时候,代码多且不易维护,我们就要用到promise。
一般情况下有异步操作时,使用Promise对这个异步操作进行封装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <script> new Promise (function (resolve,reject ) { setTimeout(function ( ) { resolve('hello world' ); },1000 ) }).then(function (data ) { console .log(data); console .log(data); console .log(data); console .log(data); console .log(data); }) </script> <script> new Promise(function(resolve,reject){ setTimeout(function(){ / /失败的时候调用reject reject('error message'); },1000) }).then(function(data){ console.log(data); console.log(data); console.log(data); console.log(data); console.log(data); }).catch(function(err){ console.log(err); / /输出错误的信息 }) </ script>
当有多个回调时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 new Promise (function (resolve,reject ) { setTimeout(function ( ) { resolve() },1000 ) }).then(function ( ) { console .log('hello world' ); console .log('hello world' ); console .log('hello world' ); console .log('hello world' ); console .log('hello world' ); return new Promise (function (resolve,reject ) { setTimeout(function ( ) { resolve() },1000 ) }).then(function ( ) { console .log('hello vue' ); console .log('hello vue' ); console .log('hello vue' ); console .log('hello vue' ); console .log('hello vue' ); return new Promise (function (resolve,reject ) { setTimeout(function ( ) { resolve() },1000 ) }).then(function ( ) { console .log('hello es6' ); console .log('hello es6' ); console .log('hello es6' ); console .log('hello es6' ); console .log('hello es6' ); }) }) })
promise三种状态:
pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且回调.then()
reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且回调.catch()
1 2 3 4 5 6 7 8 9 10 11 12 <script> new Promise (function (resolve,reject ) { setTimeout(function ( ) { resolve('hello world' ); reject('error message' ); },1000 ) }).then(function (data ) { console .log(data); },function (err ) { console .log(err); }) </script>
//第一个参数为resolve函数的处理,第二个参数为reject的处理
Promise的链式操作简写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 new Promise (function (resolve,reject ) { setTimeout(function ( ) { resolve('aaa' ); },1000 ) }).then(function (res ) { console .log(res,'第一层的10行处理代码' ); return Promise .reject('error message' ) }).then(function (res ) { console .log(res,'第二层的10行处理代码' ); return Promise .resolve(res+'222' ) }).then(function (res ) { console .log(res,'第三层的10行处理代码' ) }).catch(function (res ) { console .log(res); })
Promise的all()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <script> Promise .all([ new Promise (function (resolve,reject ) { $.ajax({ url:'url1' , success:function (data ) { resolve(data) } }) }), new Promise (function (resolve,reject ) { $.ajax({ url:'url2' , success:function (data ) { resolve(data) } }) }) ]).then(function (results ) { results[0 ] results[1 ] }) </script>
Vuex vuex是一个专门为Vue.js应用程序开发的状态管理模式。
假如多个组件都有一个变量共享的话,我们要把这个变量放到哪里,如果放到组件里面的话,当其它组件来调用这个变量的话就会显得很麻烦,如果组件多就是个大问题,所以我们都会把多个组件共享的全部变量全部存储在一个对象里面。相当于这个变量是个公共资源,当组件用到的时候,去这个公共资源里面找。
猜疑?为什么我们不自己封装一个对象,然后把公共资源放到对象里,用的时候各个组件再调用呢?
答:当我们使用对象的时候,当我们改变对象里面的值时,组件里之前调用的值是不会改变的,就像不使用单例模式前我们实例化的时候,第一次的对象值跟第二次的对象值结果不一样。每次实例化的时候都会开辟不同的内存地址,所以值是不一样的。但是Vuex就不一样了,它会响应式,即使你在对象里改变了值,它以前的值也会改变的。
所以Vuex就是一个在多个组件间共享状态的插件。
actions是用来回调的,回调一般用在后端。Devtools是用来记录state状态的,可根据Devtools来查找是哪个组件改的记录。
State 单一状态树:尽量只使用一个store,便于管理和维护。
getters :有时候我们需要从 store 中的 state 中派生出一些状态,例如对列表进行过滤并计数,我们就用到getter函数对state中的东西作出筛选和过滤,getters是内容。
mutations :相应规则:Vuex的store中的state是响应式的,当state中数据发生改变时,Vue组件会自动更新。mutation操作一般用在同步操作,如果用在异步的话,效果不是那么的好,devtools插件会捕捉不到有时。所有异步的话就要用Action。
Actions :代替mutation进行异步操作。但是它并没有跳过mutation环节,mutation环节是必不可少的。
以下为Action的步骤代码:
然后在store里进行异步:
Modules :Module就是模块的意思,因为Vue的state使用单一状态树,意味着很多状态都交给Vuex来管理,当应用非常多的时候,store对象就会变得非常复杂化,可读性差。为了解决这个问题,vuex提供了module,而每个module都有自己的states,mutations
,actions,getters,小李把这现象称为疯狂套娃模式。在调用modules的时候,就如下图调用,我用的是a模块里的东西。
在真正的开发中,如果把很多东西都放到同一个文件里面,会造成项目看着很乱,所以,我们分别为getters,mutations,actions,modules各自见自己的文件,然后使用模块导出导入的方式,进行一一调用。
axios网络模块封装 一般我们进行网络请求封装的时候都是使用jQuery-Ajax来进行的,但是在Vue中的话,我们已经引入了Vue框架,如果再引入jQuery的话,会造成资源的一种浪费,所以完全没有必要去进行,所以在Vue中我们进行网络请求的时候我们一般都使用axios框架。
jsonp :使用jsonp的原因是为了解决跨域访问的问题。JSONP的核心在于通过<-script>标签的src来帮我们请求数据,当我们的项目部署到domain1.com服务器上的时候,此项目是不能直接访问domain2.com服务器上的资料。这个时候,我们利用<-script>标签上的scr来帮助我们去服务器上请求数据,将数据当作javascript的函数执行,并在执行的过程中,传入我们需要的json,所以封装json的核心就在于我们监听windows上的jsonp进行回调的名称。
安装axios : npm Install axios –save 开发时依赖
调用格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 axios({ url:'http://123.207.32.32:8000/home/multidata' , methods:'get' }).then(function (res ) { console .log(res) }) axios.all([axios({ url:'http://123.207.32.32:8000/home/multidata' , }),axios({ url:'http://123.207.32.32:8000/home/data' , params:{ type:'sell' , page:'4' } })]).then(function (result ) { console .log(result) })
当存在多个服务器时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const instance1 = axios.create({ baseURL:'http://123.207.32.32:8000' , timeout:5000 }) instance1({ url:'/home/multidata' }).then(function (res ) { console .log(res); }) instance1({ url:'/home/data' , params:{ type:'pop' , page:1 } }).then(function (res ) { console .log(res); }) const instance2 = axios.create({ baseURL:'http://193.112.131.5:8000' , timeout:10000 })
为了避免组件对axios的框架,我们就应该创建公共的axios模块,需要时各个组件向公共axios模块调用即可。
先建个network文件,放要封装的axios模块。
然后再main.js里面进行引入。
axios拦截器 :
请求拦截器的作用是在请求发送前进行一些操作,例如在每个请求体里加上token,统一做了处理如果以后要改也非常容易。
响应拦截器的作用是在接收到响应后进行一些操作,例如在服务器返回登录状态失效,需要重新登录的时候,跳转到登录页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ##request.js文件 import axios from 'axios' export function request (config ) { const instance = axios.create({ baseURL:'http://123.207.32.32:8000' , timeout:5000 , }) instance.interceptors.request.use(function (config ) { return config },function (err ) { console .log(err); }) instance.interceptors.response.use(function (res ) { return res; },function (err ) { console .log(err); }) return instance(config); }
1 2 3 4 5 6 7 8 9 ##home.js文件 import {request} from './request.js' export function getHomeMultidata ( ) { return request({ url:'/home/multidata' }) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 ##Home.vue文件 <template> <div id="home"> <NavBar class="home-nav"> <div slot="center">购物街</div> </NavBar> </div> </template> <script> import NavBar from '../../components/common/navbar/NavBar.vue' import {getHomeMultidata} from '../../network/home.js' //如果不用default导出的话要加大括号 export default { name:'Home', components:{ NavBar }, data(){ return{ banners:[] } }, //利用生命周期函数请求数据 created(){ getHomeMultidata().then(res => { // console.log(res); this.result = res; //因为函数是局部作用域,里面的数据被调用就会被销毁,也就是说res不指向对象的地址了,所以直接把res的指向赋值给result变量,到时候即使res不指向对象的时候,result也指向那个对象的地址 }) // console.log(this.result); } } </script> <style scoped> .home-nav{ background-color: var(--color-tint); /*直接使用css预设的变量*/ color: white; } </style>
Vue常见问题 vue-cli下的项目出现Module not found: Error: Can’t resolve ‘stylus-loader’错误解决方案:
1 2 第一步:npm install stylus stylus-loader --save-dev 第二步:npm install
绑定在标签中的res:
res如果绑定在组件中,那么通过this.$refs.refname获取到的是一个组件对象
所有组件都有一个属性$el:用于获取组件中的元素
res如果绑定在普通的元素中,那么通过this.$refs.refname获取到的是一个元素对象
为什么搞res:res相当于唯一的标识符,避免有相同的类名而冲突
所有组件都有一个属性$el:用于获取组件中的元素
ES6箭头函数与普通函数的区别 箭头函数不会创建自己的this:
一般函数都有自己的this,但是箭头函数没有自己的this,它只会从自己的作用域链的上一层继承this。它会捕获自己在定义时所处的外层执行环境的this,并继承这个this值。
Vue项目开发 解决首页中Better-Scroll可滚动区域的问题。
Better-Scroll在决定有多少区域可以滚动的时候,是根据scrollHeight属性决定的:
sctollerHight属性是根据Better-Scroll的content中的子组件的高度计算的,但是
在首页中,刚开始计算scrollerHeight属性时,是没有将网络请求到的图片计算在内的。
所以计算出来的是错误的(1300+)
后来图片加载出来之后有了新的高度,但是scrollerHeight属性并没有进行更新。
所以滚动出现了问题。
如何解决这一问题?
监听每一张图片是否加载完成,只要有一张图片加载完成了,执行依次refresh()。
tabControl的吸顶效果 1.获取到tabControl的offsetTop
必须知道滚动到多少的时候才有吸顶效果,这个时候就需要获取tabControl的offsetTop
如果直接再生命周期函数mounted中获取tabControl的offsetTop,那么值是不正确的。所以我们用@load直接监听HomeSwiper组件中的img的加载。然后发出事件,在Home组件中获取正确的值。
让Home保持原来的状态 当我们离开home页,生命周期函数会执行destroy函数,组件会销毁当前状态,所以使用keep-alive。
让Home保持原来的状态:
离开时在生命周期函数deactivated中保存当前的位置信息y
进来时,在生命周期函数activated中调用原来保存的位置信息y