vue技术点归纳
1.vue是什么?
vue是一套构建用户界面的渐进式框架,核心库只关注视图层。
vue和react相同点:
1.都使用 virtual DOM;
2.提供了响应式(reactive);
3.组件化(composable)的视图组件;
4.都支持native方案;
5.都支持SSR服务端渲染;
6.都支持props进行父子组件通信;
不同之处就是:
1.数据绑定方面,vue实现了数据的双向数据绑定,react数据流动是单向的。
2.virtual dom不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。而react,每当应用的状态
被改变时,全部组件都会重新渲染,所以react需要 shouldComponentUpdate这个生命周期函数来进行控制。
3.state对象在react应用中不可变,需要使用 setState 方法更新状态;
vue中,state对象不是必须的,数据由data属性在vue对象中管理;
4.组件写法不一样,react推荐JSX; vue推荐webpack+vue-loader的单文件组件格式;
vue.js核心特点-响应的数据绑定
vue中实现组件引入流程: 第一步使用 import导入需要引入的组件文件;第二步使用components注册组件;
第三步在需要引入组件的文件中加上组件标签。
单文件组件: js, css, html存在一个文件中,称为单文件组件。
虚拟DOM:
运行的js速度很快,大量操作DOM就会很慢,在更新数据后会重新渲染页面,这样造成在没有改变数据的地方也重新
渲染了DOM节点,就造成了资源浪费。
利用在内存中生成与真实DOM与之对应的数据结构,这个在内存中生成的结构称之为虚拟DOM。
当数据发生变化时,能够计算出重新渲染组件的最小代价并应用到DOM操作上。
MVVM:
M-model数据模型, V-view视图模板, VM-view-model视图模型
vue的MVVM实例-双向数据绑定。当数据发生改变会自动更新视图,利用Object.defineProperty中的setter/getter
代理数据,监控对数据的操作。
声明式渲染与命令式渲染区别:
声明式渲染:只需要声明在哪里,做什么,无需关心如何实现。
命令式渲染:需要具体代码表达在哪里,做什么,如何实践。
1.1. MVVM响应式
2.x的响应式:
1.响应式需要做到在修改数据的时候对dom的自动更改;
2.需要在set里面自动绑定一个dom元素
3.get,set操作是通过 Object.defineProperty() 完成;
eg:
let data = { msg: 'hello' }
// 模拟实例;
let vm = {};
// 数据劫持
Object.defineProperty(vm, 'msg', {
enumerable: true, //可枚举
configurable: true, //可配置
//获取到值
get(){
return data.msg;
},
//当设置值
set(nv){
if(nv ===data.msg){ return }
data.msg = nv
//数据更改,更新DOM的值
document.querySelector('#app').textContent = data.msg;
}
})
vm.msg = 'hello world';
console.log(vm.msg);
3.x的响应式
1.通过proxy进行对象代理
2.直接监听对象,而非2.x的属性
//模拟data
let data = {
msg: 'hello',
count: 0,
}
//模拟实例
//Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(eg属性查找,赋值,枚举...)
let vm = new Proxy(data, {
get(target, key){
return target[key];
},
set(target, key, nv){
if(target[key] === nv){ return }
target[key] = nv;
document.querySelector('#app').textContent = target[key]
}
})
//测试
vm.msg = 'hello world';
console.log(vm.msg);
发布订阅者模式:即假设存在一个信号中心,某个任务执行完,就向信号中心发布publish一个信号,其他任务可以向信号
中心订阅subscribe这个信号,从而知道自己什么时候可以开始执行。
eg:
let vm = new Vue()
vm.$on('dataChange',()=>{
console.log('datachage');
})
vm.$emit('dataChange');
兄弟组件的通信过程:
使用eventBus.js,及发布订阅者模式。
观察者模式:由具体目标调度,比如当事件出发,Dep就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是
存在依赖的。
eg:
观察者(订阅者)-- Watcher
update(): 当事件发生时,具体要做的事情
目标(发布者) -- Dep
subs 数组: 存储所有的观察者
addSub(): 添加观察者
notify(): 当事件发生,调用所有观察者的 update()方法
没有事件中心。
2.node.js和npm
npm的package.json文件详解参考链接:https://github.com/ericdum/mujiang.info/issues/6/
淘宝镜像:
npm install -g cnpm --registry=https://registry.npmmirror.com
然后就可以使用cnpm命令安装模块了
cnpm intall [name]
3.vue安装
使用 npm install vue -g 或者 cnpm install vue -g 安装vue.js
使用 npm install webpack -g 安装 webpack 模板
安装vue-cli2.x脚手架:
使用 npm install vue-cli -g 安装vue-cli2.x脚手架
使用 npm install -g vue-router
创建 vue-cli2创建项目
vue init webpack 项目名
安装vue-cli3.x脚手架:
先卸载旧版本2.x: npm uninstall vue-cli -g
卸载3.x版本: npm uninstall @vue/cli -g
安装新版本:
npm install @vue/cli -g
指定版本号 npm install @vue/cli@3.12.1 不指定版本号会默认安装最新的版本
新建项目: vue create 项目名
运行项目: npm run serve
项目中新建 vue.config.js 文件,更改端口号1000
module.exports = {
//扩展配置
configureWebpack: {
devServer:{
port: 10000,
open: true, //自动启动浏览器
}
}
}
vue项目结构:
1.build:构建脚本目录
2.config: 项目配置
dev.env.js =>开发换届变量
index.js =>项目配置文件
prod.env.js =>生产环境变量
3.package.json: npm包配置文件,定义了项目的npm脚本,依赖包等信息
4.vue实例
1.EL元素节点
2.Data数据
3.Methods方法集合
4.computed计算属性
注意:如果没有给计算属性显示的返回一个值,默认会返回undefined。
由于计算属性在使用时只是一个常量值,不是一个函数,无法传参。
为什么要使用计算属性?(因为缓存)
只有计算属性引用的响应式属性发生改变时才会重新计算,如果引用的属性没有改变,则调用上一次缓存值。
而methods里的函数在每次调用时都要执行,每次值都是最新的。
5.watch 监听属性
6.生命周期
注意:在created中可以对data数据进行操作,这个时候可以进行ajax请求将返回的数据赋值给data。
在mounted中对挂载的dom进行操作。
在使用vue-router时需要使用<keep-alive></keep-alive>来缓存组件的状态,这个时候created不会被重复调用,
如果需要做其他操作,可以在activated中处理。
7.vue指令
v-model双向绑定数据
v-for循环
v-show显示与隐藏
v-if显示与隐藏(dom元素的删除添加)
v-else
v-else-if
v-bind
v-on事件
v-text读取文本不能读取html标签
v-html读取html标签
v-class类名(三种绑定方法 1.对象型 '{red: isRed}') 2.三目型 'isRed?"red":"blue"' 3.数组型 '[{red: "isRed"},{blue: "isBlue"}]'
v-style样式
v-once进入页面时,只渲染一次,不再进行渲染
v-cloak防闪烁
v-pre把标签内部的元素原位输出
5.v-model
v-model可以实现表单元素和数据的双向绑定。
v-model是一个语法糖,包含两个操作,即v-bind绑定一个value属性,v-on指令给当前元素绑定input事件。如:
<input type="text" :value="msg" @input="message = $event.target.value" />
等价于 <input type="text" v-model="msg" />
v-model和radio属性结合:
在input元素中,radio单选按钮属性,name属性用于 对提交到服务器后的表单数据进行标识。
将name属性设置为不同的值,可对两个选择按钮进行同时选择;将name属性设置为相同的值,就能实现互斥选择。
常见的表单数据绑定:
input=text文本框
input=radio单选按钮
input=checkbox复选框
input=select下拉框
修饰符:
lazy修饰符:v-model默认是在input事件中同步输入框的数据,只有改变输入框的数据,data中的数据就会立即改变。
lazy修饰符可以让数据在失去焦点或者回车时才会更新。
number修饰符:默认情况下,在输入框中无论输入字母还是数字,都会被当做字符串类型进行处理。number修饰符可以让输入框中的内容
自动转成数字类型。
trim修饰符:可过滤掉输入内容左右两侧的空格。
6.vue组件
组件系统,提供了一种抽象,可使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树。
组件需注册后才可使用,注册分为全局注册和局部注册两种方式。
全局注册:
使用 Vue.component() 方法,先传入一个自定义组件的名字,然后传入这个组件的配置。
Vue.component('button-counter', {
//组件内容
<template: '<button>click me</button>'
})
创建全局组件时,使用vue对象的component方法,这个方法接收两个参数。第一个字符串:表示组件名称,第二个为一个对象:表示组件内容。
组件中的data必须是函数:因为像vue实例那样,传入一个对象,由于JS中对象类型的变量实际上保存的是对象的引用,所以当存在多个这样的组件
时,会共享数据,导致一个组件中数据的改变会引起其他组件数据的改变。而使用一个返回对象的函数,每次使用组件都会创建一个新的对象,这样
就不会出现共享数据了。
在某些时候,vue组件会受到html的限制,如table内就只能写行列,这时候组件写进去就会无效,此时可用is属性来实现。
局部组件:
vue实例中有个选项 components 可以注册局部组件,注册后就只在该实例作用域下有效。
vue中父子组件通信方式:
prop常用
通过prop向子组件传递数据。父子组件之间的数据传递相当于自上向下的下水管子,只能从上往下流,不能逆流。即单向数据流。
父组件传给子组件数值,子组件不用对其进行修改,而是找个变量将父组件传递的数值赋值过来,然后对变量进行操作。
$emit 组件封装用的较多
$emit 触发当前实例上的事件。附加参数都会传给监听器回调,如: this.$emit(eventName, params)
.sync 语法糖
.sync修饰符
$attrs 和 $listeners(组件封装用的较多)
provide 和 inject(高阶组件/组件库)
允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在上下游关系成立的时间始终生效。
主要适用于在一些深入嵌套的组件中,子组件想要获取父组件的部分内容,此时可在父组件中使用provide来提供数据,在子组件中使用
inject来获取并使用这些数据。
全局事件总线mitt库:全局事件总线可以用于非父子组件之间的通信,如与兄弟组件或者兄弟组件的子组件进行通信。
安装mitt库,封装eventbus.js工具类: npm install mitt
import mitt from 'mitt'
const emitter = mitt()
export default emitter;
// 触发事件
emitter.emit(eventName, {msg:'hello'})
// 监听事件
emitter.on(eventName, (params)=>{
// 获取传递过来的数据啦
})
EventBus:即声明一个全局vue实例变量 EventBus,把所有的通信数据,数据监听都存储到这个变量上,这样就达到组件间实现数据共享,
类似vuex,适合小项目,大而复杂项目更适合用vuex。
插槽slot
例如:
Hello组件
// 默认插槽
<slot>默认插槽内容</slot>
// 具名插槽, 使用特殊属性 name 来定义具名插槽
<slot name="title">显示具名插槽内容</slot>
// 作用域插槽
/**如何使用: 在template或者html元素上使用特殊的 slot-scope 属性可以接受传递给插槽的 prop
作用域插槽的作用: 是为了更方便在父级作用域中访问子级作用域拥有的数据。
即在父组件中使用子组件中的数据。
*/
// 绑定在<slot>元素上的属性被称为 插槽 prop,不包含name
<slot name="title" :data="{msg: 'hello'}"></slot>
<div slot="title" slot-scope="{data}">{{data.firstName}}</div>
v2.6以上版本,slot使用方式变为 v-slot
异步组件:
是vue的一种性能优化的方法,可以实现组件的按需加载。组件通过 import函数 引用,什么时候需要什么时候加载。
有利于项目的性能优化,提高页面的加载速度。路由懒加载就是使用了异步组件的原理。
使用import引入:
components: {
IsAsyncDemo:()=>import('./IsAsyncD.vue')
}
如果在异步加载过程中出现了错误,
可安装babel动态导入插件:npm install --save-dev babel-plugin-syntax-dynamic-import
在.babelrc中配置使用此插件:
{ "presets": ['env'], 'plugins': ['syntax-dynamic-import'] }
动态组件:
让多个组件使用一个挂载点,并且组件间可以动态切换,这个挂载点是 component 标签。
即在 component 标签上添加一个 is 属性, 属性值是控制组件间的切换的。
可配合 keep-alive 使用,这样切换的时候不用频繁渲染了(keep-alive是vue的内置组件,可以包含动态组件,当组件之间进行切换时,可
保持组件的状态,在内存中 缓存不活动组件的实例,而不是销毁他们。并且自身也不会渲染成一个DOM元素,不会显示在父组件链中)。
使用规则:1.语法 is='component-name' 动态对应 data中对应的组件值 2.data中代表组件的属性值必须是 引入组件名。
例如:
//hello.vue
<template>
<div>
<component :is=Dynamic" />
// 循环动态加载
<div v-for='(val,key) in newData' :key="key">
<component :is='val.type' /> // 对应子组件名字
</div>
</div>
</template>
<script>
import Dynamic from './Dynamic.vue'
import IsImage from './IsImage.vue'
import IsVideo from './IsVideo.vue'
import IsText from './IsText.vue'
export default {
components: { Dynamic, IsImage, IsVideo, IsText },
data(){
return {
newData: [{type: 'IsImage'},{type: 'IsVideo'},{type:'IsText'}]
}
}
}
</script>
7.vue-router
路由中有三个基本概念: route, routes, router。
route:指的是一条路由,是单数。
routes: 是一组路由,即把每一条路由组合起来,形成一个数组。
router:是一个机制,相当于管理者,它来管理路由。
客户端路由有两种实现方式:基于hash和基于 html5 history api。
页面实现路由:
在vue模板中,使用 router-link 和 router-view 来对应点击和显示部分。
配置路由:
const routes = [{path:'/home',component:Home},...]
Vue.use(VueRouter)
const router = new VueRouter({ routes })
export default router
动态路由: 动态部分以 : 开头。如:
const routers = [
{path:'/user/:userId', component:user}
]
如果想获取动态路由中的参数,需使用 this.$route.params.userId
this.$route 匹配的是routes中处于活跃的路由。
vue3.0项目常用依赖配置-安装路由,例如:
//安装依赖
npm install vue-router@next --save
// src文件夹下创建 router 文件夹,router文件夹下创建 index.js文件
// 配置router,进入index.js文件
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
// 定义路由组件
import HelloWorld from '../components/HelloWorld.vue'
// 定义路由配置
const routes = [{path;'/',redirect:'/HelloWorld'},{path:'/HelloWorld',name:'HelloWorld',component:HelloWorld}]
// 创建路由实例
const router = createRouter({
//采用hash模式
history: createWebHashHistory(),
//采用history模式
history: createWebHistory(),
routes
})
export default router;
编程式导航:即通过调用JavaScript形式的API实现导航的方式。如:普通网页的location.href。
this.$router.push('hash地址')
this.$router.go(n)
router.push()方法的参数规则:
router.push('/home') //字符串-路径名称
router.push({path:'/home'}) //对象
router.push({name:'hello',params:{msg:'world'}}) //命名的路由-传递参数
router.push({path:'/about',query:{msg:'hello'}}) //带查询参数,变成 /about?msg=hello
声明式导航:即通过点击链接实现导航的方式。
路由分为前端路由+后端路由,路由的本质就是对应关系。
后端路由概念:根据不同的用户url请求,返回不同的内容。
后端路由本质:url请求地址与服务器资源之间的对应关系。
SPA(single page application):
1.后端渲染(存在性能问题)2.ajax前端渲染
3.SPA实现原理:基于url地址的hash(hash的变化会导致浏览器记录访问历史的变化,但是hash的变化不会触发新的url请求)
前端路由概念:根据不同的用户事件,显示不同的页面内容。
前端路由本质:用户事件与事件处理函数之间的对应关系。
vue-router包含的功能有:支持HTML5历史模式或hash模式;支持嵌套路由;支持路由参数;支持编程式参数;支持命名路由。
路由重定向:通过路由规则的redirect属性,指定一个新的路由地址,设置路由的重定向。
注意: alias 别名的作用,类似于重定向的效果。
alias给组件取一个别名,利用这个别名去访问页面。
redirect和alias区别:
redirect:观察url的变化,redirect是直接改变了url的值,把url变成了真实的path路径。
alias:url路径没有发生变化,这种情况更友好,让用户知道自己访问的路径,只是改变了<router-view>中的内容。注意一下alias别名不能在
path路径为 / 中使用,如果使用的话,网页不会显示内容。
{ path:'/hello', name: 'Hello', component: Hello, alias: '/sayHello' }
路由模式:
vue-router默认使用的是hash模式,url地址后面会带一个#。
hash模式与history模式区别:
hash模式:hash原理其实是浏览器监听了一个 onhashchange 的方法来改变页面的跳转
window.onhashchange = function(event){
console.log(event.oldURL, event.newURL)
let hash = location.hash.slice(1);
}
使用hash模式页面会给路由状态记录下来,可理解为浏览器缓存机制,存储起来了。
缺点:只能改变#后面的参数来实现路由的跳转。
history模式:
history模式包括 back, forward, go 三个方法,对应浏览器的前进,后退,跳转操作。
缺点:怕刷新页面,因为刷新是实实在在的去请求服务器了。
前置路由守卫:
vue-router提供了三大类钩子函数来实现路由守卫:
全局钩子函数(beforeEach, afterEach):
beforeEach: 接收三个参数,分别是 to, from, next; to指的是即将进入的路由对象; from值得是正要离开的路由对象;
next指的是路由的控制参数。
next一共有四种调用方式:
1.next()一切正常进入下一个钩子;2.next(false)取消路由导航
3.next('/login')当前路由被终止,进入一个新的路由导航
4.next(error)路由导航终止并且错误会被传递到router.onError()注册过的回调中
afterEach: afterEach比beforeEach少一个next参数。一般用来重置页面滚动条位置。
//全局路由改变后钩子
router.afterEach((to, from)=>{
//将滚动条恢复到最顶端
window.scrollTo(0,0);
})
路由独享的钩子函数(beforeEnter):
路由独享是 指定的路由才有这些钩子函数,通常这类路由独享的钩子函数是在路由配置文件中进行配置,只能设置改变前的钩子,不能
设置改变后的钩子。
组件内钩子函数(beforeRouterEnter, beforeRouterUpdate, beforeRouterLeave):
beforeRouteEnter(to, from, next):
在路由进入前调用,因此此时的vue实例还没有创建,所以 beforeRouteEnter是唯一一个不能使用this的钩子函数。
beforeRouteUpdate(to, from, next):
在路由发生修改的时候进行调用,比如动态路由传参时。vue实例已被创建,可以访问到实例。
beforeRouteLeave(to, from ,next):
在路由离开该组件时调用;vue实例已被创建,可访问到实例。
//demo.vue,在组件内部调用钩子函数,如:
<template>
<div>
<div>home-page</div>
<router-link to='xx'>xx</router-link>
<router-link to='xxx'>xxx</router-link>
</div>
</template>
<script>
export default {
// 路由进入前调用
beforeRouteEnter(to, from, next){
window.document.title = 'weclome';
next()
},
// 路由修改时调用
beforeRouteUpdate(to, from, next){},
// 路由离开时调用
beforeRouteLeave(to, from, next){},
data(){
return{}
}
}
</script>
后置路由守卫:
vue-router是基于路由和组件的:
1.路由用户设定访问路径的,将路径和组件映射起来。
2.在vue-router的单页面应用中,页面的路径的改变就是组件的切换。
使用 npm install vue-router --save 进行安装。
然后可通过Vue.use()来使用它,如:
先导入路由对象并且调用Vue.use(VueRouter);再创建路由实例,并且传入路由映射配置;最后在Vue实例中挂载创建的路由实例。
具体代码实现步骤:
//先创建 router实例,创建 router文件夹,并创建index.js文件,
// index.js文件中
//配置路由相关信息
import VueRouter from 'vue-router'
import Vue from 'vue'
//通过Vue.use(插件),安装插件
Vue.use(VueRouter)
//创建VueRouter对象
const routes = [
...
]
const router = new VueRouter({
routes, //配置路由和组件之间的应用关系
mode: 'history', // 这里默认是hash模式,如果想使用 history 模式,则进行配置即可。
})
//将router对象传入到vue实例
export default router
//main.js文件
//引入router文件
import router from './router/index'
...
new Vue({
...
router,
}).$mount('#app')
//App.vue文件
<template>
<div>
<router-link to='xx'>xx</router-link> // 此标签会被渲染成一个 a 标签
// to用于指定跳转的路径;tag可以指定<router-link>之后渲染成什么组件,replace不会留下history记录,
如果指定replace,后退键返回不能回到上一个页面中。active-class当<router-link>对应的路由匹配成功时,会自动给
当前元素设置一个router-link-active的class,设置active-class可以修改默认的名称。
<router-view></router-view> // 根据当前路径,动态渲染出不同的组件
</div>
</template>
路由的默认路径,配置映射如: { path: '/', redirect: '/home' } 即可。
改变路径的方式有两种:hash 和 history,默认情况下,路径的改变使用的是 url 的 hash 。
路由懒加载方式如下:
方式一:结合vue的异步组件和webpack代码分析
const Home = resolve=>{ require.ensure(['../components/Home.vue'], ()=>{
resolve(require('../components/Home.vue'))
}) }
方式二:AMD写法
const Home = resolve => require(['../components/Home.vue'], resolve)
方式三:ES6写法
const Home = ()=>import('../components/Home.vue')
传递参数的方式:使用 params 和 query。
8.vuex状态管理
vuex:一个专为vue.js应用程序开发的状态管理模式。
使用vuex注意事项:1.应用层级的状态应该集中到单个store对象中。2.提交mutation是更改状态的唯一方法,并且这个过程是同步的。
3.异步逻辑都应该封装到action里面。
vuex的五个核心概念: state, getters, mutatioins, actions, modules。
state: state是唯一的数据源,只要通过import导入vuex,然后通过vue.use(vuex),就可以通过this.$store拿到vuex的对象,state就是
在vuex里面定义的属性。基本数据。
在computed中,使用 mapState 辅助函数可获取state里面的属性。如:
computed: {
...mapState({})
}
getters: 通过getters可派生一些新的状态。从基本数据派生的数据。
mapGetters辅助函数可将store中的getters映射到局部计算属性。如:
computed: {
...mapGetters(['countDouble','countTest'])
}
// 如果想将一个getter属性另取一个名字,可使用对象形式,如:
mapGetters({
double: 'countDobule'
})
mutations: 更改vuex的store中的状态的唯一方法就是提交mutation。同步操作。提交更改数据的方法。
mapMutations辅助函数,可以在组件中使用 this.$store.commit('xx') 提交mutation。
或者使用 mapMutations辅助函数将组件中的 methods映射为 store.commit 调用
actions: action提交的是mutation,而不是直接变更状态。异步操作。像一个装饰器,包裹mutations。
action函数接受一个与store实例具有相同方法和属性的context对象。因此可以调用 context.commit提交一个mutation,
或者通过 context.state 和 context.getters 来获取state 和 getters。
action 通过 store.dispatch 方法触发,如:
store.dispatch('increment')
mapActions辅助函数将组件的methods映射为 store.dispatch调用。
this.$store.dispatch('xx')
modules:可将vuex的store对象分割成模块modules。模块化Vuex。如:
const moduleA = {state:{},...}
const moduleB = {state:{...},...}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
//使用
this.$store.state.a //moduleA的状态
使用vue-cli脚手架创建项目后,运行 npm install vuex 命令安装vuex。
安装完成后,在项目中创建 store文件夹,创建index.js
//index.js
//引入vue,vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
items: [{num:1,name:'xx'}]
},
getters: {
numChange(state){
return state.items.forEach(item=>item.num+=10)
}
},
mutations: {
// mutations中的方法需要使用 commit 配合回调实现。
numTurn(state){
state.items.forEach(item=>{ item.num+=2 })
},
increment(){
}
},
actions: {
increment(context){
context.commit('increment')
}
}
})
//在其他组件内部使用
methods:{
handleClick(){ this.$store.commit('numTurn') }
}
//main.js
import store from './store/index'
new Vue({
...,
store,
})
9.vue-cli
vue-cli脚手架安装:
1.先检查是否安装了node, 使用命令 node -v
2.使用 npm install -g vue-cli //-g的意思是全局安装。
3.使用 vue -V 可查看版本
卸载vue-cli:
vue cli包名称由 vue-cli变为了 @vue/cli。如果想升级vue-cli3且已全局安装了旧版本vue-cli,需要先通过
npm uninstall vue-cli -g 或者 yarn global remove vue-cli卸载它。
初始化项目:
创建项目: vue create vue-project
启动项目:
cd vue-project
npm install
npm run serve
vue-cli搭建项目,项目文件配置介绍:
node_modules: 安装依赖代码库
src: 存放源码
static:存放第三方静态资源
package.json:
{
"name":"项目名称",
...
"scripts": {
...
//执行命令,配置脚本
},
"dependencies":{},//项目依赖
"devDependencies":{},//编译需要的依赖
}
index.html,main.js,App.vue: 入口文件
vue是MVVM框架,是以数据驱动的,不操作DOM,通过将DOM和数据绑定,利用Object.defineProperty中的setter,getter代理数据,监控对数据
的变化,当数据改变时更新DOM。
vue项目构建有两种方式,构建大型应用应使用命令行工具构建,另一种是直接通过<script>标签引入。
使用vue-cli命令行工具构建项目流程:
npm install vue
npm install -g vue-cli
vue init webpack vue-project
cd vue-project
npm install
npm run dev
npm run build
vue-cli即是一个基于webpack构建,可让用户快速初始化一个项目的工具。
关于vue-cli3信息:
vue-cli3包括 vue.config.js, vue-cli-service, 插件, ui。
使用 vue create 初始化一个项目
关于 vue.config.js配置:
//vue.config.js文件
module.exports = {
//部署应用时的基本路径 URL, baseURL
publicPath: process.env.NODE_ENV==='production'?'xx':'xxx',
//build构建文件的目录
outputDir: 'dist', //当运行 vue-cli-service build时生成的生成环境构建文件的目录
// build时放置生成的静态资源(js,css,img,fonts)的目录
assetDir: '',
// 指定生成的 index.html 的输出路径
indexPath: 'index.html',
//默认在生成的静态资源文件名中包含hash以控制缓存
filenameHashing: true,
//构建多页面应用,页面配置
pages: {
index: {},
subpage: '',
}
//是否在开发环境下通过
lintOnSave: process.env.NODE_ENV !== 'production',
// 对内部的webpack配置,如修改,增加loader选项,链式操作
chainWebpack: ()=>{},
// css处理
css: {},
//所有 webpack-dev-server的选项都支持
devServer: {
open: true, //设置浏览器自动打开项目
port: 8080,//设置端口
proxy: { //设置代理
'/api': {
target: 'xxx',
changeOrigin: true, //开启跨域
pathRewrite: { // 替换掉请求路径的 /api 为 ''
'^/api': ''
}
}
},
},
//第三方插件选项
pluginOptions: {}
}
10.自定义指令
全局注册:
1.在vue实例前写,如:
Vue.directive('focus', {
inserted: function(el){
el.focus()
}
})
//注意: 全局注册 directive 是没有加 s 的,局部注册是有 s 的。
局部注册:
在vue实例里面写,如:
directives: {
content: {
inserted:el=>{
el.value = '请输入内容'
}
}
}
在页面中用 v-指令名引用即可,如:
<input type="text" v-focus v-content />
钩子函数:
bind:只调用一次,指令第一次绑定到元素时调用。可进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用。(仅保证父节点存在,但不一定已被插入文档中)
update:所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前
componentUpdated: 指定所在组件的VNode及其子VNode全部更新后调用
unbind: 只调用一次,指令与元素解绑时调用
11.axios
axios是一个基于 promise 的HTTP库(用于HTTP请求)。
axios特性:
1.支持promise API
2.拦截请求和响应
3.转换请求数据和响应数据
4.取消请求
5.自动转换JSON数据
6.客户端支持防御XSRF
使用 npm install axios进行安装。
axios基础用法,常见的请求方法有:get, post, put, patch, delete
get: 一般用于获取数据
如:
axios.get(url, {
params: {}
}).then(res=>{//返回数据})
或者
axios({
method: 'get',
url: 'xx',
params: {},
}).then(res=>{})
状态码304是重定向,正常情况下,第一次访问接口的时候返回的都是200,当你第二次访问接口的时候,如果数据没有变化,那么浏览器会自动
识别并返回一个状态304,代表数据没有更改,重定向到刚刚访问的资源,这样子操作加载更快。
post: 提交数据(表单提交+文件上传)
如:
//post常用的请求数据data格式有两种: application/json , form-data表单提交(图片上传,文件上传)
const data = {}
axios.post(url, data).then(res=>{})
axios.post({
method: 'post',
url: 'xx',
data: data
}).then(res=>{})
// form-data请求
const formData = new FormData()
for(let key in data){
formData.append(key, data[key])
}
axios.post(url, formData).then(res=>{})
put: 更新数据-所有数据推送到后端
patch: 更新数据-将修改的数据推送到后端
delete: 删除数据
axios并发请求:同时进行多个请求,并统一处理返回值。
//例如:axios.all('参数是一个数组'), axios.spread('回调函数')
axios.all([
axios.get(url),
axios.get(url)
]).then(axios.spread(res,err)=>{
console.log(res)
})
关于对axios进行封装实例配置,拦截器,取消请求等操作
//进入src/api/request.js文件
import axios from 'axios'
import qs from 'qs' //node中自带的qs模块,用于数据格式转换
import store from '../store'
let baseURL;
if(process.env.NODE_ENV==='development'){
baseURL='开发环境'
}else{
if(process.env.type==='test'){
baseURL='测试环境'
}else{
baseURL='正式环境'
}
}
const instance = axios.create({
baseURL: '后台服务地址',
timeout: 60000, //请求超时时间1分钟
responseType: 'json',
})
//请求拦截
instance.interceptors.request.use(config=>{
//自定义header,添加token或自定义参数
config.headers.token = store.state.app.token
config.headers.timestamp = new Date().getTime();
if(config.method=='post'){
//axios-post方式默认是json格式提交数据,如果想使用 application/x-www-form-urlencode数据格式提交,则需要用
// qs.stringify()进行转换。注意,不过这种方式不太灵活
config.data = qs.stringify(config.data)
}
return config
}, error=>{
return Promise.reject(error)
})
//请求响应后拦截
instance.interceptors.response.use(response=>{
const code = response.status
if(code===200){
return Promise.resolve(response)
}else{
return Promise.reject(response)
}
//或者直接返回数据
return response
},error=>{
return Promise.reject(error)
})
export default instance //如果使用导出的方式,则可以直接在api接口文件中进行封装接口,例如进入userApi.js
//userApi.js
import instance from './request.js'
export function getUserInfo(data){
return instance({
url:'接口地址',
method: 'post',
headers: {
'Content-Type': 'application/json'//设置请求头请求格式为json
},
data
})
}
export function getList(data){
return instance({
url:'',
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded', //设置请求头请求格式form
},
data
})
}
//使用方式 test.vue
import qs from 'qs'
import {getList} from '@/api/userApi.js'
created(){
const params = {}
getList(qs.stringify(params)).then(res=>{
//接口数据是 application/x-www-form-urlencoded,参数要进行qs转换
})
}
//封装get方法
export function Get(url, params){
return new Promise((resolve, reject)=>{
instance.get(url, {
params: params
}).then(res=>{
resolve(res.data)
}).catch(error=>{
reject(error.data)
})
})
}
//封装post方法
export function Post(url, data){
return new Promise((resolve, reject)=>{
instance.post(url, data).then(res=>{
resolve(res.data)
}).catch(error=>{
reject(error.data)
})
})
}
//进入 src/api/index.js
import { Get, Post } from '@/api/request'
export default {
getList: (params) => {
return Get('接口地址', params)
}
postList: (data)=>{
return Post('接口地址', data)
}
}
// 进入 src/main.js
可进行全局注册
import Api from './api/index'
Vue.prototype.$axios = Api
this.$axiost.postList((id:1)).then(res=>{
//返回数据
})
或者直接引用,如
// 进入 test.vue
import api from '@/api/index'
methods: {
async queryGetList(){
const res = await api.getList({id: 1})
}
}
12.webpack打包工具
1.模块化规范:
CommonJS规范:
每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量,函数,类都是私有的,其他文件不可见。例如:
创建 module 文件夹:
-a.js文件
-b.js文件
ES6模块化规范:
ES6使用 export 和 import 来导出导入模块。例如:
创建 moduleEs6 文件夹:
-a.js文件,做导出模块
export functioin getList(){ }
//或者这样:
export default {
getList(){},
...
}
-b.js文件,做导入模块
import { getList } from './a.js'
//或者这样
import a from './a.js'
使用方式: a.getList()
AMD,CMD...
webpack:是一个前端资源加载/打包工具。根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。
可将多种静态资源js,css,less转换成一个静态文件,减少了页面的请求。
webpack的基本能力有:处理依赖,模块化,打包。
处理依赖:方便引入第三方模块,让模块更容易复用,避免全局注入导致的冲突,避免重复加载不必要的模块。
合并代码:把各个分散的模块集中打包成大文件,减少HTTP请求次数。
各种插件:babel把ES6+转化为ES5-,eslint可以检查编译时的各种错误等。
其他打包工具有grunt/gulp,他们的核心是Task,即可以配置一系列的task,并且定义task要处理的事务,然后让他们依次来执行这些task,
而且让整个流程自动化。
plugin:插件,通常是用于对某个现有的架构进行扩展。
loader和plugin区别在于:loader主要用于转化某些类型的模块,是一个转化器。plugin是插件,它是对webpack本身的扩展,是一个扩展器。
参考链接:
https://edu.csdn.net/skill/vue/vue-fdef334aa3f047e09f2fb94cbb42a069?category=1029