不会就问就查询!科技利民,学海无涯2!
1、vue.js的安装
- 方式一:直接CDN引入
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
- 方式二:下载和引入
- 方式三:npm下载
2、vue指令
<p v-once>{{message}}</p>//值不再改变
<p v-pre>{{message}}</p>//原样输出
<p v-clock>{{message}}</p>//结合[v-clock]{display:none;}样式,实现斗篷效果;
<p v-html="message">我会被覆盖</p>
v-html='属性名':动态绑定输出内容,能识别解析html标签,覆盖标签文本
<p v-text="message">我会被覆盖</p>
v-text='属性名':动态绑定输出内容,正常输出内容,覆盖标签文本
<li v-for="(item,index) in array">{{item}}</li>
v-for='':实现for循环
<button v-on:click='add'>add事件</button>
v-on:[dom事件]='事件名':绑定(点击)事件,可以绑定其他事件
<img v-bind:src="imgUrl" alt="">
v-bind:[html属性]='属性名':动态绑定属性值
3、指令详解
4、ES6中对象字面量增强(简写)写法
- 属性增强写法:
let name = 'aa';
let obj = {name};//增强写法,就是直接使用外面定义好的变量名;
let obj = { name:name };//常规写法
- 函数增强写法
methods:{
add:function(){},//常规写法:键值对
add(){},//增强写法
}
5、针对vue中元素复用:使用key属性,避免输入框切换时value输入值被带过来;
6、key属性
- 不能用index索引值,因为索引是变化的;
- 要用唯一标识;
- 作用:为了高效的更新虚拟DOM;
- 列表中for循环数组时如果在中间插入新元素,不加key会元素复用,导致后面所有元素都会改变;加key属性,之前的元素就不会改变,只需要创建一个新的元素然后放到指定位置;(与JS相关不加引号是变量,加引号是字符串;html、css相关加引号也是字符串,但是与vue结合相关的都是变量)
<ul>
<li v-for="(item,index) in array" :key="item.id">{{item}}--{{index}}</li>
</ul>
7、vue中哪些数组方法是响应式的?
响应式:
- push(…item):向数组结尾添加若干元素
- pop():删除数组最后一个元素
- shift():删除数组第一个元素
- unshift(…item):向数组开头添加若干元素
- splice(开始下标[>=0],要删除的个数[>=0],‘插入的新值’,‘插入的新值’,,,,):删除、插入、替换数组元素;(返回新数组,同时改变原数组)
非响应:
- 直接按索引改变数组某个值;
app.array[0]='aaa';
- 借助splice实现改变值的响应效果;
- Vue内置方法:
Vue.set(修改的对象,索引值,修改后的值);
- eg:
Vue.set(this.array,0,'aaa');
8、JS数组的高阶函数
- filter( fn( item){} ):将原数组中符合条件的元素返回到新数组中,最后返回一个新数组;
- 参数为回调函数,回调函数传入的值是数组中每一项;
- 内部回调函数返回true:就把当前传入的item添加到新数组中;
- 内部回调函数返回false:直接过滤掉,进行下一个item操作;
<script>
const arr = [1,2,3,4,5];
let newArr = arr.filter(function(n){
return n<3;
});
console.log(newArr);//[1,2]
</script>
- map( fn( item){} ):将数组中每一项进行运算,把得到的新值赋值到新数组中,最后返回一个新数组;
const arr = [1,2,3,4,5];
let newArr = arr.map(function(n){
return n+3;
});
console.log(newArr)//[4, 5, 6, 7, 8]
- reduce( fn( pre,item){},[pre的初始值] ):对数组中每一项挨个操作,最后返回一个值;
const arr = [1,2,3,4,'a'];
let tatol = arr.reduce(function(pre,item){
return pre + item;
},0);
console.log(tatol)//10a 字符串
- 编程范式:命令式编程、声明式编程
- 编程范式:面向对象编程(第一公民:对象)、函数式编程(第一公民:函数)
函数式编程特点(链式编程):
const nums = [1,2,3,4,100]
let total = nums.filter(n=>n<100).map(n=>{n*2}).reduce((per,n)=>n + per,0) /:20
9、指令修饰符:事件修饰符、双向绑定修饰符;
v-on:click.stop="事件名";
v-model.number=" data中属性名 "
10、vue组使用件的注册
常规注册:
第一步:创建组件构造对象:Vue.extend()
;(现在一般不直接写这个,采用语法糖形式,这一步可以省;)
第二步:注册组件(全局/局部)
第三步:使用组件:<myCpn></myCpn>
- 全局组件:在Vue实例外注册,其他的Vue实例对应的 div 元素内也可以使用;
- 局部组件:在Vue实例内注册,只有对应的 div 元素内才能使用;
<script>
const cpn = Vue.extend({
template:`
<div>
<p>我是父组件</p>
</div>
`
});//创建组件构造器
Vue.component('cpn1',cpn);//注册全局组件,一般用不着这个
const app = new Vue({
el:'#app',
data:{},
components: {
myCpn:cpn
}
})//注册局部组件
</script>
补充一、语法糖形式:将创建组件模板与注册合二为一步;
Vue.component('cpn1',{
template:`<div><p>我是语法糖组件</p></div>`
});//语法糖:创建并注册全局组件
-----------------------------------------------------------------------
const app = new Vue({
el: '#app',
data: {},
components: {
cpn1: {
template: `<div><p>我是语法糖组件aa</p></div>`
}
}
})//语法糖:创建并注册局部组件
补充二、语法糖基础上再做模板的分离:将模板中html代码分离出去,通过id属性引入;(两种方式)
方法一:
<script type="text/x-template" id="aaa">
<div>
<p>我是语法糖组件分离写法</p>
</div>
</script>
方法二:(推荐)
<template id="aaa">
<div>
<p>我是语法糖组件分离写法</p>
</div>
</template>
--------------------------------------
<script>
const app = new Vue({
el: '#app',
data: {},
components: {
cpn1: {
template: '#aaa'
}
}
})
</script>
补充三、创建父组件子组件:创建子组件构造器,必须在父组件之前定义;
const cpn2 = Vue.extend({
template:`
<div>
<p>我是子组件</p>
</div>
`
});//创建子组件构造器,必须在父组件之前定义
const cpn = Vue.extend({
template:`
<div>
<p>我是组件</p>
<Cpn><Cpn>
</div>
`,
components:{
Cpn:cpn2
}//在父组件中注册子组件,并使用
});//创建父组件构造器
12、组件访问:父组件访问子组件、子组件访问父组件;
- 父访问子:
- 方式一:
this.$children[]
:通过下标找子组件,子组件改变可能会找错; - 方式二:
this.refs.aaa.[属性名]
+ref="aaa"
:给子组件设置ref,按名查找;
- 方式一:
- 子访问父:
- 方式一:
this.$parent.[属性名]
:访问父组件data中数据 - 方式二:
this.$root.[]属性名
:直接访问Vue实例data中数据
- 方式一:
13、插槽slot元素的使用
2.6.0版本之前
插槽:
// 父组件
<cpn>
<button>默认插槽</button>
<button slot='aa'>子组件具名插槽</button>
<template slot-scope='slot'> // 将子组件传来的 date对象 数据存放在 slot对象 中
<p slot='bb'>{{slot.data}} 作用域插槽</p>
</template>
</cpn>
---
// 子组件 cpn.vue
<slot></slot> // 默认命名:default
<slot name="aa"></slot>
<slot name="bb" :data="number"></slot>
2.6.0版本后
插槽:改变了父组件的使用插槽的方式;
- 使用
<template v-slot:[name]=[接收子组件的值]> [替换内容] </template>
包裹指定名字插槽的替换内容;- 注意: v-slot 只能添加在
<template v-slot >、<组件标签 v-slot>(独占默认插槽时)
上- 具名插槽语法糖:
v-slot:header 可以被重写为 #header
,# 后面必须有值不能只写 #
// 父组件
<cpn>
<template v-slot:default="slotProps">
<p>子组件默认插槽(写法一)</p>
</template>
<template v-slot="slotProps">
<p>子组件默认插槽(写法二)</p>
</template>
<template v-slot:aa>
<h1>子组件具名插槽</h1>
</template>
<template v-slot:bb="slotProps">
<p>{{slotProps.data}}</p>
</template>
</cpn>
---
// 子组件
<div>
<slot :data="number"></slot> // 默认命名:default
<slot name="aa"></slot>
<slot name="bb" :data="number"></slot> //属性名data可以随便命名;
</div>
独占默认插槽缩写语法:
// 被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用
<cpn v-slot:default="slotProps">
{{ slotProps.user }}
</cpn>
----再简化:
<cpn v-slot="slotProps">
{{ slotProps.user }}
</cpn>
扩展:
解构写法:
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
解构重命名:
<current-user v-slot="{ user: person }">
{{ person.firstName }}
</current-user>
解构附默认值:用于子组件没有传插槽prop时
<current-user v-slot="{ user = { firstName: 'Guest' } }">
{{ user.firstName }}
</current-user>
15、模块化开发:核心-(在js文件中)导入导出
历史:
JS文件分散,不同文件全局变量冲突—>使用匿名函数,不同文件代码不可复用—>简单模块化,文件中导出对象,暴露出去成全局变量—>统一规范的模块化(CommenJS(nodejs、webpack采用的就是这个规范)、AMD、CMD、ES6的modules)
- CommenJS:在nodejs环境中可以识别解析它的语法;
// 导出单个成员:
module.exports = {
a:1,
b:'aaa'
};//导出单个成员
// 导出多个成员:内部为了让开发人员书写方便: var exports= module.exports;
exports.a = 'aaa';
exports.b = 'bbb';
// 导入: 就是导入 module.exports
var obj = require('./aa.js');//这里导出相当于 var obj = module.exports;
obj.a;//'aaa'
var { a , b } = require('../aa.js'); // 解构写法
- ES6的模块化:es6语法可以识别;
首先,导入导出之前先确保不同JS文件分隔开- 可以不在同一个文件中引入;
- 或者使用模块化标签:声明是模块化的,相当于各自不干扰;
<script src="aaa.js" type="module"></script>
<script src="bbb.js" type="module"></script>
然后,在JS文件中按需导入导出:
aaa.js导出:
export { a,b }//导出方式一,定义后统一导出
export let a = 'haha'//方式二,定义时直接导出
export function aa(){}//导出函数
bbb.js导入:
import { a,b } from "./aaa.js" /导入时写在{}中,不能改名
可以改名的导入导出:
export default a//只能导出一个变量
import [自主命名] from "./aaa.js" /导入时可以改名,不叫{}
统一导入:当需要导入很多变量时
import * as aa from "./aaa,js"//将所有要导入的变量存在aa对象中使用;
16、脚手架cli (command-line interface命令行界面)
vue-cli2、vue-cli3
- 环境依赖:node.js、webpack
- 安装:直接安装cli3即可
npm install -g @vue/cli
;通过拉取cli2模板·,实现在cli3环境下可以创建cli2项目;npm install @vue/cli-init -g
- 创建vuecli2项目:
vue init webpack [项目名]
,不用中文和大写字母 - 创建vuecli3项目:
vue creat [项目名]
,不用中文和大写字母 - vue两个版本:runtime-only(简化版(默认版本),不允许JS文件中使用template,JS文件中的不能编译)、runtime-compiler(内容多,允许js文件中有template)
runtime-only main.js:
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
render: h => h(App)
})
------
runtime-compiler main.js:
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
17、路由vue-router
背景:
- 后端渲染阶段:后端(服务器)路由
- 后端渲染:早期,浏览器发送网址给服务器,服务器通过通过正则对url进行匹配(后端路由),找到对应的页面(包含html、css、后端代码),并交给controller处理渲染好,交给浏览器直接展示;
- 后端路由:就是url与页面的对应关系,由服务器匹配;
- 优点:后端渲染有利于seo优化;
- 缺点:主要由后端人员开发,前后端代码混淆;所有页面都在服务器,请求不同页面要发请求;
- 前后端分离阶段:前端渲染、后端路由
- ajax出现;静态资源服务器(html、css、js,浏览器请求);数据服务器(用于api请求);
- html、css、js、数据都是在浏览器渲染,所以是前端渲染;
- 优点:前后端责任清晰,后端专注数据、前端专注交互与可视化;而且后端的数据api接口可供多端使用;
- 缺点:所有页面仍然都在服务器,每次使用不同页面都要请求(页面请求、AJAX数据请求这是两种);
- 前端路由阶段:前端渲染、单页面SPA
- 前端路由同样是改变url,但是不会传到服务器(会使用history或hash技术让浏览器不去发请求),是给本地js(所有.vue组件都在打包后的js文件中)用来选择展示哪个组件使用;
- 一般整个spa页面不能刷新(刷新会向服务器发请求,history或hash技术不能控制刷新时发送请求,只是自身路由添加时管用),刷新会出现404,所以要使用nginx配置重定向来解决不能刷新问题;
- 缺点:一次性请求所有资源;不利于SEO优化;
前端路由:防止url改变发送请求
- hash哈希模式(利用:#)
location.hash='aaa'
:本质是修改window.location的href属性;页面不会刷新;
- history模式(h5)
history.pushState({},'','home')
:进栈出栈,支持浏览器点击前进后退;history.back()
:后退一步(出栈一个),等价history.go(-1)
history.forward()
:前进一步,等价history.go(1)
history.go(-n)
:后退n步history.go(n)
:前进n步history.replaceState({},'','about')
:替换,不能后退、前进
vue -router 路由
- 设定访问路径,将路径与组件映射起来;
- vue-router的单页面应用中,页面的路径的改变就是组件的切换;
vue -router 路由的安装与使用
- 安装vue-router:
- 通过vue-cli
npm install vue-router --save
- 使用:
- 1、:导入路由对象,并且调用
Vue。use(VueRouter)
;/router/index.js
- 2、:创建路由实例,并且传入路由映射配置;
/router/index.js、App.vue
- 3、:在Vue实例中挂载创建的路由实例:相当于给Vue原型对象添加:
$router(路径)、$route(活跃的路由)
属性;main.js
- 1、:导入路由对象,并且调用
main.js:
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
- 2、映射配置:/router/index.js、App.vue
- 2.1、:创建路由组件:
HelloWorld.vue
- 2.2、:配置路由映射:组件和路径映射关系
- 2.3、:使用路由:通过
<router-link>
和<router-view>
- 2.1、:创建路由组件:
2.2、index.js:
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
export default new Router({
routes: [
{
path: '',
redirect: '/hello'
},
{
path: '/hello',
name: 'HelloWorld',
component: HelloWorld
}
],
mode:'history'
})
2.3、App.vue:
<template>
<div id="app">
<img src="./assets/logo.png">
<router-link to='/hello'>hello</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
vue -router 路由的注意点
<router-link>
标签点击后的自带类:class="router-link-exact-active router-link-active"
可以通过router实例改变类名:
index.js:
export default new Router({
mode: 'history',
routes: [
{
path: '/hello',
name: 'HelloWorld',
component: HelloWorld
}
],
linkActiveClass:'active'
})
- 通过代码
<button @click="helloClick">哈喽</button>
实现<router-link to="/hello" tag="button" replace >hello</router-link>
App.vue:
<script>
export default {
name: 'App',
methods: {
helloClick(){
// this.$router.push('/hello');
this.$router.replace('/hello')
}
}
}
</script>
vue -router 动态路由:vue文件
- 动态路由:在路由跳转是传参数;
- 方式一:params传参;第一步:通过
v-bind
动态绑定to
属性实现路径拼接;第二步:在路由配置路径中使用:[参数名]
接收,第三步:在活跃组件中computed
计算属性使用this.$route.params.[参数名]
获取参数值,或者利用{{$route.params.[参数名]}}
; - 方式二:query传参;
<router-link :to="{ path:'/profile',query:{name:'zzz',age:12}}" >档案</router-link>
- 方式一:params传参;第一步:通过
vue -router 动态懒加载与嵌套:index.js
const Home = ()=>import('../components/Home.vue')
const HomeNews = ()=>import('../components/Homes/HomeNews.vue')
const HomeMessage = ()=>import('../components/Homes/HomeMessage.vue')
const routes = [
{
path:'',
redirect: '/home'
},
{
path:'/home',
component:Home,
children:[
{
path:'news',
component:HomeNews
},
{
path:'message',
component:HomeMessage
}
]
}
]
vue -router 全局导航守卫:(守卫不需要定义,直接传参执行的)index.js中定义(需要借助vue-router实例执行)、也可以在main.js中定义
- 前置守卫guard(钩子函数hook):
router.beforeEach((to, from, next) => { next();[自己的代码] })
:点击路由要进入这个组件时触发,next
决定是否让进;刚加载时不执行; - 后置守卫guard(钩子函数hook):
router.afterEach( (to,from) => {}))
:进入某个路由组件后执行;刚加载页面时也执行,from是/根目录
;
main.js:
new Vue({
el: '#app',
router,
render: h => h(App)
})
router.beforeEach((to, from, next) => {
document.title = to.matched[0].meta.title;
/* console.log(to);
console.log(from); */
next('/login');//(可以利用判断条件)强制跳到login组件,不写默认进入当前组件
})
vue -router 导航守卫补充:路由独享守卫、组件内守卫
- 路由独享守卫:
beforeEnter
,刚加载页面时也执行;(类似全局导航方法的参数)
index.js:
{
path:'news',
name:'HomeNews',//常用于<keep-alive>做筛选时
component:HomeNews,
beforeEnter:(to,from,next)=>{
console.log(to);
next();
}
},
- 组件内部守卫:自身vue文件中定义;(也类似全局导航方法的参数)
export default {
name:'Profile',
beforeRouteEnter(to, from, next) {
console.log(this);
console.log(from);
console.log(to);
next()
},
beforeRouteUpdate(to,from,next){
next()
},
beforeRouteLeave(to, from, next) {
console.log(this);
console.log(from);
console.log(to);
next()
}
}
vue -router 路由补充:状态保存 keep-alive
- keep-alive是Vue内置组件,用于使被包含的组件保留状态,避免重新渲染;所以可以
<keep-alive></keep-alive>
;- 内部属性:
include包含、exclude排除
<keep-alive exclude="Home,Profile"></keep-alive>
,这里就用到了组件内部的name
属性;
- 内部属性:
- 包住
<router-view/>
,让所有匹配到的视图组件都会被缓存;
<keep-alive><router-view/></keep-alive>
- 结合两个钩子函数:活跃
activated
、不活跃deactivated
;
export default {
name:'Home',
activated () {},
deactivated () {}//在进入新组建后执行,
//离开前可以使用组件内部守卫:beforeRouteLeave(to, from, next) { next() }
}
18、Vuex状态管理模式
19、Axios网络请求模块
20、响应式原理