vue总结
Posted m1754171640
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue总结相关的知识,希望对你有一定的参考价值。
1、库和框架的区别
库:jquery
本质上就是一些列函数的集合,将一些函数封装到一个独立的就是文件中
在使用的jquery的时候,是由开发人员说了算的,也就是说开发人员起到了主导作用,而jquery是辅助完成相应的功能的
框架:vue
框架是一套完整的解决方案,项目中用到的一些功能在框架内部都已经提供好了
在使用框架的时候,只要将我们自己写的代码,放到框架合适的地方去,框架会在合适的时机主动调用我们写好的代码,框架指定好了一套规则,我们开发人员,按照这套规则写代码就可以了
也就是说:在使用vue的时候,是由框架说了算的,也就是框架起到了主导作用
库和框架的区别?
本质区别:控制反转,谁起到了主导作用
在使用框的时候,有开发人员起到了主导作用
在使用框架的时候,由框架起到了主导作用
2、vue可以看作是一个mvvm模式的框架的
它内部实现了数据双向绑定
双向绑定的两个方面:
1、视图---》数据 只要视图中的内入改变了,那么对应的数据也会自动跟着改变
2、数据---》视图 只要数据发生改变,对应视图内容也会自动更新
主要是保持视图和数据的同步
mvvm模式:适合带有视图的应用,比如前端(页面)、桌面端的应用等
这个模式最早出现在微软wpf技术中
3、插值表达式 mustache语法
作用:将数据插入到当前位置
插值表达式中能够使用任意的js表达式,不能出现语句(if-else/for)
它不能出现在html属性中
4、常用指令
v-model 指令的作用:用来实现数据的双向绑定,让文本框的值和数据双向绑定到一起,只要任意一个改变,另外一个都会自动跟着改变
(只能用在表单元素中,在不同的表单元素中,功能不同)
v-text 用来设置文本内容,相当于(innerText),如果元素中有默认内容,默认内容会被覆盖掉
v-html 如果字符串中的内容,包含了html字符串,就用v-html(也会覆盖)
v-bind 给html属性动态设置内容
操作样式 的两种方式
v-bind:class="{ 要添加的类名: 布尔值 }"
v-bind:style="{}"
v-on: click=‘fn‘ v-on(简写@) 绑定事件的前缀 click事件名称
( eg @click="foo" 注意:如果没有给事件处理程序显示通过 () 来传递参数,那么,第一个参数默认表示:事件对象 ; 如果传递参数了需要使用vue中约定的$event 来获取事件对象 )
v-show v-if 都是控制元素的展示与隐藏
v-show 值为true显示,false 隐藏;通过css的display :none 来控制展示与隐藏的
v-if 值为true显示, false隐藏,是通过移除元素,把html结构中这个标签删除来隐藏的,操作了dom
v-else / v-else-if 类似于js中的if-else
必须先使用v-if指令,后面才能使用v-else 或 v-else-if 指令
v-cloak 解决vue中刚渲染页面时候插值表达式闪烁的问题
给元素添加v-cloak指令后,手动添加display:none的样式,会让该元素隐藏,当vue解析模版内容的时候,首先会把 {{}} 解析成对应的数据,并且将 v-cloak 指令,从元素中 移除掉。 指令移除后, display: none 的样式 就不再生效了,所以,最终在页面中就看到数据内容了
v-once 该指令所在元素中的内容,只会被解析成一次
v-pre 指令所在的元素中的内容,一次都不会被解析,告诉vue跳过该指令所在的元素,不去遍历该元素以及所有的后代元素
v-for 能够遍历指定的数据(数据或对象),重复生成指令所在的标签
<li v-for="(item, key, index) in obj"> item 表示对象中属性的值 key 表示对象中属性的键 index 表示索引号
只有使用了v-for指令,就要添加key属性,key属性的值是唯一的,一般就是用数据中的id作为key值
为什么要加key?
使用v-for不加key 会有临时状态出现错乱的现象,(比如在文本框输入文字的时候,会错位)
为什么会出现这样的问题?
vue中使用v-for渲染列表数据的时候,默认采用的是就地复用的策略,出于性能的考虑
什么是就地复用?
默认情况下不添加key,实际情况是以每一项的索引号作为key,但是每一项的数据的索引号是会发生改变的,所有插入数据后,数据就错乱了
总结:
1、默认不添加key的时候,就是按照索引号来复用元素,会有问题
2、添加key的时候,应该保证key是唯一的且不可变的,一般就是每一项的id,此时就是按照id来复用的
3、如果列表渲染不依赖于临时状态,此时,就可以使用index作为key值
5、事件修饰符
.prevent 是一个事件修饰符,用来阻止浏览器的默认行为
6、vue采用异步的方式更新DOM
也就是说:数据发生改变后,DOM没有立即被更新的,而是等到数据不再发生改变的时候,一次性的将所有的DOM更新
为什么采用异步方式更新DOM? 性能考虑
因为如果数据发生改变就更新dom,这会进行大量的dom操作,引起多次浏览器的重绘和重排 ,性能影响大
在某些情况下,可能需要在修改数据后,立即获取到DOM内容,此时,可以通过vue中的$nextTick() 来获取更新后的DOM
$nextTick ()参数是一个回调函数,这个回调函数会在dom更新后立即触发
7、vue动态添加数据
添加单个 vue.$set(1,2,3)
参数一:表示要给哪个对象添加响应式数据
参数二: 表示要添加的属性名称叫什么
参数三: 表示要添加属性的值为多少
添加多个 Object.assign() 方法来批量添加
创建一个新的空对象,将后面所有对象参数中的属性,拷贝到新对象中,并且将这个新对象返回,这样做,不会改变原始对象的结构
Object.assign({}, vm.obj, { age: 19, gender: ‘male‘, weight: 180 })
8、监视数据变化 watch
watch :{
? 对象的健表示要监视的数据
? xx: {
? handler 是固定的名称
? 第一个参数:当前最新值
? 第二个参数 : 表示上一次的值
? handler( newVal, oldVal){
? }
? }
}
如果监听对象类型数据的变化,需要添加deep属性,来实现深度监听,才能够监听到对象中属性的改变
如果监听的是对象类型的数据,那么,newVal和oldVal 表示同一个对象
9、vue是生命周期
10、json-server 是nodeJS的一个包,作用:用来提供假数据
在没有服务器接口的时候,通过这个工具提供一个假数据,当有了服务器的接口,再替换成为真正的真数据即可
使用步骤:
1、全局安装 npm i -g json-server
2、准备应该json文件
3、在json文件中,运行命令: json-server 文件名 --watch
json-server 提供的接口是 REST API (restful) 配合postman 插件使用
查询:get请求
? 查询所有数据: http://localhost:3000/json文件名
? 查询某一条(把参数id带着):http://localhost:3000/json/2
添加: post 请求
修改:PATCH / PUT 请求
? 修改也要带着id
如果是PATCH 请求,只需要将要修改的数据作为参数发送给接口即可
如果用的是PUT 请求,需要将所有的数据全部发送给接口
删除 : DELETE请求
带着参数
11、axios的使用
axios 是一个专门用来发送ajax请求的包
axios 和 vue没有任何关系,axios可以在任何地方用来发送请求
它是一个http客户端,可以在浏览器或node中来发送请求
使用步骤:
1、安装 npm i axios
2、 在页面中引入axios
发送GET请求 可以通过.then()方法来获取接口返回的数据
axios.get("http//localhost:3000/json").then(res=> {})
?
传递参数方式
第一种方式
axios.get("http://loaclhost:3000/json?_id=1&_name=‘zs‘").then(res => {})
第二种方式
axios.get("http://loaclhost:3000/json",{
params:{
_id:1,
_name:‘zs‘
}
}).then(res=> {})
第三种方式 字符串模版
axios.get(`http://loacllhost:3000/json/${id},${name}`).then(res=>{})
?
?
发送post请求
axios.post("http://localhost:3000/json",{
name:‘zs‘,
age: 18
}).then(res=> {})
?
?
发送PATCH、PUT请求
axios.patch("http://localhost:3000/json/2",{
name:‘ls‘
}).then(res => { })
?
?
发送DELETE请求:
axios.delete("http://localhost:3000/json/3")
12、vue中的更新机制(虚拟DOM 和 diff 算法)
vue中的dom更新是异步的
vue中更新DOM是只更新变化的地方,也就是说,只有页面中用到的数据发生了改变,那么页面中的数据才会被更新,如果页面中的数据没有改变,那么,页面中的元素是不会被更新的
这样做的优势:降低了DOM 更新,也就是减少了浏览器的重绘与重排,提升了性能
问题: vue如果知道要更新哪个地方? 虚拟dom + diff算法实现的
为什么不使用原生的DOM对象,而是自己手动创建一个虚拟DOM对象?
因为原生DOM对象中有很多属性,而这些属性中绝大部分与页面的表现没有任何的关系
diff算法是如何更新DOM的?
1、vue第一次编译模版的时候,会生成一个虚拟的DOM 对象
2、当有数据发生改变后,vue又会生成一个新的虚拟DOM对象
3、通过diff算法,来对比更新前后两个虚拟DOM对象,找到不同的地方
4、只将变化的地方,更新到页面中,这样,用户就看到页面更新了
虚拟DOM如何产生的? vm._vnode
模版 + data = > 虚拟DOM
模版 + 新的data => 新的虚拟的DOM对象
13、vue中的过滤器
vue中的过滤器是用来对数据进行格式化处理的
(数据格式化就是让一个数据按照我们指定好的格式输出就是数据的格式化)
vue中的过滤器分为:
全局过滤器
局部过滤器
过滤器的语法: 通过 | 管道符号 ,来使用过滤器
作用:使用date 过滤器,来格式化curTime 这个数据,最终,将格式化后的数据输出
<p>{{ curTime | date }}</p>
注册全局过滤器
Vue.filter(‘data‘,input=>{
return `${input.getFullYear() - ${input.getMonth() + 1}}`
})
?
第一个参数:表示过滤器的名称
第二个参数:是一个回调函数,表示过滤器的代码逻辑,即在使用过滤器的时候,这个回调函数就会执行
返回值:表示 格式化的数据的内容,这个内容会展示在页面中
?
配合moment插件使用,更简单
Vue.filter(‘date‘,(input, format = ‘YYYY-MM-DD HH:mm:ss‘, arg1, arg2) => {
// 回调函数第一个参数:表示要格式化的数据
// 后面可以有任意多个参数,这些参数都是在 使用过滤器 的时候传入的
console.log(‘传入参数为:‘, format, arg1, arg2)
return moment(input).format(format)
}
)
?
局部过滤器
filters: { date(input, format = ‘YYYY-MM-DD HH:mm:ss‘) {
return moment(input).format(format)
}
}
?
全局过滤器 和 局部过滤器的区别
全局过滤器可以在任意vue实例(组件)中使用)0
局部过滤器只能在当前vue实例(组件)中使用
14、组件化开发
将一个完整的页面,划分成一个个的组件,最终由一个个组件来完成整个页面的开发,这个过程就是组件化开发
优势: 复用
15、组件的两种注册方式
全局组件和局部组件
注册全局组件
Vue.component(‘hello‘,{
templata: //templata 作用:指定组件模版
`
<div>这是一个组件</div> // 只能出现一个根节点
`
})
第一个参数:表示组件名称
第二个参数:是一个配置对象
?
注册局部组件
components: {
jack: {
template: `
<div>这是 hello 组件的子组件 jack</div>
`
}
}
?
?
两种方式的区别:
1 全局组件可以在任意的地方使用
2 局部组件只能在所属组件的 模板 中使用
16、组件通讯
组件是一个独立且封闭的个体,也就说:默认情况下,组件只能使用自身的数据,而不能使用外部的数据,在组件化开发的过程中,两个组件通讯,就需要通过组件通讯机制来解决
1、父到子通讯
第一步:在子组件标签中添加要传递的属性
第二步:在子组件中通过props显示指定要接收的数据
第三步:就可以在子组件中直接使用父组件中的数据了
props 是只读的,在组件中使用的时候,只能读取,不能修改props的值(赋值)
如果props是一个引用类型的数据,不能直接赋值,但是,可以修改引用类型中某个属性的值
单向数据流 :是说组件之间数据流动方向是从父组件流动到子组件的,父组件可以通过props将数据传递给子组件,并且,当父组件中的这个数据改变后,这个改变后的数据会自动的再流动到子组件中,也就是说,子组件会自动接收到最新的props数据,并且自动更新
?
props命名的规则
1、使用纯小写字母
2、传递属性的时候,使用-连接多个单词,在子组件中通过驼峰命名法来接收这个数据
2、子到父通讯
第一步:父组件提供一个方法,这个方法由子组件调用
第二步:父组件将这个方法传递给子组件
第三步:子组件调用传递过来的方法(触发自定义事件 this.$emit)
第四步:将要传递的数据,作为方法参数,进行传递,这样,父组件中的方法就会被调用,并且可以通过形参,来拿到子组件中传递过来的数据了
3、非父子组件通讯
第一步:创建一个事件总线(const bus = new Vue())
第二步:哪个组件要接收数据,就注册事件
bus.$on(事件名称,()=> {})
第三步:哪个组件要传递数据,就触发事件
bus.$emit(事件名称,要传递的数据)
注意:注册和触发事件的bus 是同一个,事件的名称也是相同的
实际上不管两个组件是什么关系,都可以通过bus方式来实现通讯!
17、SPA(single page application)单页面应用程序
概念: 一个web应用中只有一个 页面组成
也就是说:将原来由多个页面配合完成的功能,现在使用一个页面来实现
多页面应用程序(MPA multiple page application)
概念: 一个web应用中有多个页面组成
SPA的技术点:
1、ajax
2、哈希值
3、hashchange事件
为什么使用SPA?
1、加载内容更少,因为一些相同的页面部分(头部、侧边栏等)不需要重复加载
2、用户体验更好,可以通过loading效果,告诉用户数据加载中
3、减少服务器的压力,因为加载内容少了,服务器需要处理的内容少了,压力就小了
SPA的缺点:也就是ajax的缺点
不利于SEO(搜索引擎优化) 它是服务端渲染的
18、路由 的基本使用
使用步骤
1、安装路由 npm i vue-router
2、在页面中引入路由的js文件
3、创建路由实例
4、将路由实例与vue实例关联到一起
5、配置路由规则
6、在页面中 指定内容展示的位置(路由出口)
7、在页面中指定路由的入口(导航菜单 a 标签)
<body>
<div id="app">
<!-- 指定路由入口: -->
<ul>
<!-- to属性 和 路由规则中的 path 相匹配 -->
<li><router-link to="/home">首页</router-link></li>
<li><router-link to="/about">关于</router-link></li>
</ul>
?
<!-- 指定路由出口: -->
<router-view></router-view>
</div>
?
<script src="./vue.js"></script>
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
<script>
/*
路由的使用:
1 安装路由: npm i vue-router
2 在页面中引入路由的js文件(注意:在引入 Vue 之后)
3 创建路由实例
4 将路由实例与Vue实例关联到一起
5 配置路由规则
6 在 页面中 指定组件内容展示的位置( 路由出口 )
7 在 页面中 指定路由的入口(导航菜单 a标签)
*/
?
// 注册home组件
const Home = Vue.component(‘home‘, {
template: `
<div>这是 Home 组件</div>
`
})
// 快速简洁的注册组件:
const About = {
template: `
<h2>这是 About 组件</h2>
`
}
?
const router = new VueRouter({
// 通过该配置配置路由规则
routes: [
// 每一个路由规则都是一个对象
// path 表示路由规则的路径,不要带有#
// component 用来指定展示的内容,是一个组件
{ path: ‘/home‘, component: Home },
{ path: ‘/about‘, component: About }
]
})
?
const vm = new Vue({
el: ‘#app‘,
data: {},
// 将路由实例与Vue实例关联到一起
router
})
</script>
</body>
路由内部执行流程
1、点击页面中的router-link(也就是a标签),浏览器地址栏中的哈希值发生改变
2、当哈希值发生改变的时候,vue路由就监听到了这个变化
3、vue路由获取到当前的哈希值,将这个哈希值与配置好的路由规则path进行匹配
4、当匹配成功了,就把当前path对应的组件渲染到页面的router-view路由出口的位置
5、这时,用户就可以在页面中看到页面的内容变化
路由规则匹配是按照 routes数组中索引号的顺序的,一个个进行匹配,如果前面的路由匹配成功了,那么,后面的路由规则,就不会再匹配
默认路由:/
{path : ‘ /‘ , component :Home}
路由重定向:
进入页面,这个路由会被默认匹配到,直接通过redirect 配置项,跳转到 / home; 这个路由,最终,展示的 /home 对应的组件:Home
{ path: ‘/‘, redirect: ‘/home‘ },
{ path: ‘/home‘, component: Home }
路由的入口高亮
因为router-link会给当前匹配的导航菜单(路由入口)添加高亮的类名,所以我们只需要给类名添加高亮的样式,就可以实现菜单高亮效果了
router-link-exact-active, router-link-active 这是自动生成的类名,只需要给他添加上高亮的样式就可以了
如果要使用第三方的样式库(比如:bootstrap ),此时可以在路由中添加linkActiveClass 配置项,来将默认的高亮类名修改为样式库中对应的高亮的类名,这样,路由就可以配合样式库来使用了
<router-link to="/" exact>Index</router-link> exact表示精确匹配,只有当哈希值 和 to 属性的值,完全相同的时候,这个router-link才会被添加高亮
默认没有exact属性的时候,高亮类名添加的方式是模糊匹配,只要哈希值中包含了to属性的值,那么,这个router-link就会被添加高亮类名
注意:精确匹配和模糊匹配只对样式名称有效,对路由的匹配规则无效
动态路由匹配 :将多个哈希值映到同一个组件的情况,应该使用路由参数
eg {path : ‘/news/:id‘,component : NewsInfo}
js中获取路由参数 (利用$route.params.id 可以获取路由参数 )
vue复用了组件,能提升性能,但是复用组件有个问题,created钩子函数只会在第一次的时候执行,复用的时候不会在被触发
当切换路由的时候,获取不到路由的参数,所以这个时候需要利用watch来监视路由参数的变化
watch : {
$route(to,from){
对路由变化做出响应
from 从哪里
to 切换到哪去
}
}
19、webpack 模块化打包(构建工具)
两大职能:
1、打包(构建)
①语法转换
? 把less --> css -->让浏览器识别
? es6 ------>es5 --->浏览器可以使用
? TypeScript(TS) ---> es5 ---> 浏览器可以解析
②文件压缩、合并
? js、css、html压缩
? css、js文件合并,比如将多个css文件合并为1个css文件,减 少了请求次数,加快响应速度
? ③提供了开发期间的服务器
? 自动打开浏览器,监听文件变化,自动刷新浏览器等(将 一些繁琐、复杂、重复的机械性工作自动处理好,我们只需要关注业务逻辑,实现对应的功能)
2、模块化
? 在webpack 看来,所有的资源都是模块;它为前端提供了模块化开发的环境,有了webpack之后,我们可以像写NodeJS代码一样,写前端代码;引入js/css等文件即可(每个js文件都是一个独立的模块,模块之间的作用域是隔离的,需要导入和导出来实现两个模块之间的数据共享)
? webpack 能够识别现在前端所有的模块化语法,不管是require、js(AMD)、nodeJS(CommonJS)、es6等语法,webpack都认识,全都可以解析
? webpack不仅实现JS的模块化,也实现了css、图片、视频等所有前端资源,可以使用模块化的方式来处理
webpack的基本使用步骤 一
1、创建一个文件夹 webpack-basic
2、npm init -y 初始化一个package.json文件
3、安装webpack: npm i -D webpack webpack-cli
4、在package.json 文件中的scripts配置项中,添加一个脚本命令
"scripts": {
"build": "webpack ./src/js/main.js --output ./dist/bundle.js"
},
build 表示构建、打包
webpack 入口文件路径 --output 出口文件路径
5、在终端中,通过 npm run build 来执行上面配置好的build 命令;最终打包好的内容就会放在 dist/bundle.js 目录中
webpack基本使用二
1、安装:npm i -D webpack webpack-cli
? webpack 是webpack工具的核心包
? webpack-cli 提供了命令行程序中打包的命令:webpack
2、在package.json中的scripts添加一个脚本命令:
? webpack 入口文件路径 --output 出口文件路径
? webpack ./src/js/main.js --output ./dist/bundle.js
3、在终端中,执行命令 npm run build
-D 参数的说明
-D 是 --save-dev 的简写,表示:作为项目开发期间的依赖,它会在package.jsoon文件中生成一个devDependencies 配置项,其中:dev 是development 的简写,表示开发的意思
一般一些工具性质的包,都是通过-D参数来安装的
?
什么情况下会使用 -D 参数?
如果一个包,只是在开发期间使用,应该使用-D参数,来按照这个包
(如果我们写的项目代码中,没有用到这个包的代码,这个包应该通过-D 参数来安装 比如 webpack)
?
?
-S 参数的说明
-S 是 --save的简写,表示:项目依赖项,也就是我们写的项目代码中,直接使用了这些包的代码
比如 jquery / expresss
eg npm i express --save
npm i express -s
npm 版本在5以上的,可以省略不写
一般可以不用写,默认的,直接写成 npm i express
为什么要将webpack 命令放在scripts?
因为无法直接运行 webpack ./src/js/main.js --output ./dist/bundle.js命令
为什无法直接执行webpack 命令?
因为webpack不是全局安装的,所以,终端中无法直接识别webpack 这个命令
为什么将webpack命令放在scripts中就可以运行了
因为scripts脚本命令在执行的时候,会自动的将webpack命令所在目录,临时的添加到系统环境变量中,并且在运行命令后,自动将临时变量移除
为什么不全局安装webpack
1、比如:电脑中有两个项目,一个项目用的是webpack 4的版本,一个项目用的是webpack 3的版本,全局安装不能兼顾,只能顾及到一个
2、全局安装的包,不会在package.json文件中体现出来,当多人合作完成一个项目的时候,别人无法知道你全局安装了什么包,会导致 项目无法运行
两个环境的说明
--mode development 开发期间的环境 --> 在项目开发期间使用 ,代码没有被压缩
--mode production 生成环境(线上环境) --> 项目上线时对项目打包,使用的环境,会对代码压缩和优化
命令行语法简化
webpack 可以省略出口文件路径,会自动生成dist文件夹,并且入口文件叫什么名字,出口文件也叫什么名字
webpack 配置 第一种方式利用命令行
使用步骤:
1、在项目根目录中,创建webpack的配置文件,webpack.config.js
注意:这个配置文件名称是固定好的
2、在这个配置文件中添加webpack配置
const path = require(‘path‘)
?
// 导出一个配置对象, webpack 会自动读取这个配置对象的配置
module.exports = {
// 入口文件路径
entry: path.join(__dirname, ‘./src/js/main.js‘),
?
// 出口文件路径
output: {
// 出口文件所在目录
path: path.join(__dirname, ‘./dist‘),
// 出口文件的名称
filename: ‘bundle.js‘
},
?
// 模式
mode: ‘development‘
}
3、在scripts中,修改build命令:"build":"webpack"
4、在终端中执行命令 : npm run build
webpack-dev-server的基本使用,为使用webpack的时候,提供了开发期间的服务器环境
webpack-dev-server的使用步骤 (命令行的方式)
1、安装 : npm i -D webpack-dev-server
2、使用webpack-dev-server
3、在package.json中添加一个新的scripts
? "dev":"webpack-dev-server"
4、在终端中运行命令: npm run dev
webpack-dev-server做了什么?
在项目根目录中开启了一个服务器,默认的服务器地址为: http ://localhost:8080
1、自动开启了一个服务器
2、自动打开服务器
3、自动打开指定目录中的页面
4、能够修改默认端口号
5、热更新
6、自动监视文件变化,自动刷新浏览器等
webpack-dev-server的配置项:(在scripts中修改)
1 开启服务器的时候,能够自动打开浏览器
"dev": "webpack-dev-server --open"
2 打开浏览器的同时,默认展示 index.html 页面
"dev": "webpack-dev-server --open --contentBase ./src"
3 dev-server 这个命令,是不会生成 dist 目录的,此时,再引入 dist/bundle.js 文件就没有意义了
问题: bundle.js,也就是打包好的内容去哪了??? 被放在 开启服务器 的根目录中了,在页面中,可以直接通过 /bundle.js 来引用打包好的文件
为什么不放在磁盘中,而要放在服务器内存中? 性能,因为 内存 性能要比 磁盘 快很多!
CPU -> 一级缓存/二级缓存/三级缓存 -> 内存 -> 固态硬盘 / 机械硬盘
4 在修改代码后,保存文件的时候, dev-server 会自动刷新浏览器
自动监视文件变化,自动刷新浏览器
5 修改默认端口号
"dev": "webpack-dev-server --open --contentBase ./src --port 3000"
6 热更新
修改了哪个文件内容,就只更新哪个文件内容,页面不会刷新
"dev": "webpack-dev-server --open --contentBase ./src --port 3000 --hot"
webpack-dev-server 配置文件的使用方式:
webpack-dev-server 配置文件的使用方式: 1 在 webpack.config.js 中,添加配置项 2 修改 package.json 文件中的 dev 脚本:
"dev": "webpack-dev-server --hot"
3 在终端中执行命令: npm run dev
在webpack.config.js的配置
// dev-server配置:
devServer: {
// 自动打开浏览器:
open: true,
// 指定打开哪个目录中的 index.html 页面
contentBase: path.join(__dirname, ‘./src‘),
// 指定端口号
port: 3001,
// 热更新:
// 第一步:
hot: true
},
?
// 指定插件:
plugins: [
// 热更新第二步:
new webpack.HotModuleReplacementPlugin()
]
webpack 命令 和 webpack-dev-server 命令的区别:
1 webpack 命令是对项目进行打包,会生成一个 dist 目录,输出到磁盘中 2 dev-server 命名是开发期间使用的,也会对项目构建,提供服务器环境,内容没有输出到磁盘中,而是放在 开启的服务器内存 中
什么时候用webpack命令? 项目完成了,要对项目打包上线,此时应该使用该命令,打包后,只需要将 dist 目录中的内容部署到服务器中即可,就完成了项目上线
npm run build
什么时候用 webapck-de-server命令? 开发期间
npm run dev
html-webpack-plugin 插件的说明
作用:
1 根据我们指定的模板文件,在内存中生成一个新的页面
2 然后,在这个新的页面中,自动引入 js/css 等文件
并且,最终在浏览器中打开的页面,就是该插件自动生成的页面
使用步骤: 1 安装: npm i -D html-webpack-plugin 2 在 webpack.config.js 中添加配置项
// 如果使用了 html-webpack-plugin 插件,那么,这个配置项就可以省略了,
// 因为,此时,打开的页面已经变为 html-webpack-plugin 插件在内存中生成的页面了
// 指定打开哪个目录中的 index.html 页面
// contentBase: path.join(__dirname, ‘./src‘),
?
// 添加 html-webpack-plugin 插件实例:
new HtmlWebpackPlugin({
// 指定 html 页面作为模板
template: path.join(__dirname, ‘./src/index.html‘)
})
非js静态资源的打包处理:
eg : css/less/图片/字体等
默认情况下,webapck只能处理js这类的静态资源,而对于非js的静态资源需要使用专门loader来处理
处理css文件(webpack.config.js中添加配置项)
1、 安装: npm i -D style-loader css-loader2、 在 webpack.config.js 中,添加配置项来打包处理 CSS 文件
webpack处理css文件的过程:
1、webpack 分析main.js这个入口文件
2、当webpack解析到require(‘../css/index.css’)就知道这个css文件是当前项目的依赖
3、webpack会遍历module中的所有rules,分别使用每一个规则中的test的正则来与当前导入模块的路径进行匹配
4、当正则与路径匹配成功,就使用use指定的loader 来处理这个文件
5、正确处理后,css文件就生效了
// 打包处理非JS资源:
module: {
// 在 rules 中添加处理资源的配置项:
rules: [
// 1 处理 CSS 资源:
// use 执行的顺序是:从右往左,也就是:先指定 css-loader 再执行 style-loader
// 1.1 css-loader 负责读取 CSS 文件内容,将其转化为一个模块
// 1.2 style-loader 读取模块中的样式内容,创建一个 style 标签,将读取到的样式作为style标签内容,插入到页面的 head 标签中
{ test: /.css$/, use: [‘style-loader‘, ‘css-loader‘] }
]
}
处理less文件
1、安装 npm i -D less less-loader? less 是用来把 less语法转化为css? less-loader 依赖与less 包,用来读取less文件,内部调用less 文件,转化less语法
2、在webpack.config.js 添加配置
// 2 处理 less 资源:
// less-loader 读取less文件,将其转化为 CSS 语法
{ test: /.less$/, use: [‘style-loader‘, ‘css-loader‘, ‘less-loader‘] }
打包处理图片 文件:
步骤:
1、安装: npm i -D url-loader file-loader
? url-loader 或 file-loader 二选一即可
2、在webpack.config.js中添加配置项
注意:
1、默认情况下,图片在经过url-loader 处理后,会被转化为base64编码的格式
为什么要转为base64编码格式?
因为使用base64后,图片就直接被嵌入到css样式中,可以减少一次额外的图片请求;但是不是所有的图片,都需要被转化为base64格式的;只有那种很小图片才需要转化为base64,如果是大图片,不需要转换为base64,否则,加载一个大的文件,可能比额外的请求一次图片还有慢
2、使用file-loader 处理图片
file-loader 处理后:对文件进行重命名
我们自己写的样式:background-image: url(../images/1.jpg);
file-loader处理后的: background-image:url(9158a14a88d0041fcc6890fa543173ce.jpg)
file-loader内部使用MD5机制来对文件进行重命名
MD5特征码提取(加密算法),只要是同一个文件,不管使用MD5提取多少次,得到的字符串特征码都是一样的
为什么要使用MD5机制来对文件进行重命名?
因为对于图片相同,但是文件命名不想同的情况,实际上就是加载了同一张图片,但是如果不进行重命名就会发送两次请求,如果重命名,这两个文件得到的MD5是一样的,此时,就只会发送一次请求,也就是减少了请求次数,加快网站的性能
处理 图片 资源: 这是利用url-loader处理图片
注意:如果值有一个 loader,此时,可以直接使用 字符串形式
{ test: /.(png|jpg|jpeg|gif)$/, use: ‘url-loader‘ }
{
test: /.(png|jpg|jpeg|gif)$/,
use: {
loader: ‘url-loader‘,
options: {
// 单位:字节(b)
// 1024字节 = 1kb
// 规则:
// 1 如果图片的文件大小,比 limit 值小了,此时,图片会被转化为base64
// 2 如果图片的文件大小,与 limit 相等或更大,此时,图片不再被转化为base64
// 内部会自动调用 file-loader 来处理图片
limit: 8106
}
}
},
这是利用file-loader处理图片
{ test: /.(png|jpg|jpeg|gif)$/, use: ‘file-loader‘ }
MD5的说明
在前端,为了安全,可以对密码等敏感数据加密,加密后在发送给服务器,实际上,注册用户后,数据是存储在数据库中,数据库中存储的密码也是加密后的密码
MD5只能加密,不能解密的!
MD5 + 随机字符串(自己指定) = MD5 => 32位字符串 + 随机字符串 = MD5 => 密码
打包字体文件:
打包方式 与 打包图片相同,都可以用 url-loader 或 file-loader 来处理
步骤:
1 安装: npm i -D url-loader file-loader
2 在 webpack.config.js 中添加规则
字体图标的使用:
1 导入 字体图标 库的样式
2 在 index.html 页面中,给元素添加 字体图标 提供的样式名称
webpack.config.js中的配置
// 处理字体图标文件 { test: /.(eot|svg|ttf|woff|woff2|otf)$/, use: ‘url-loader‘ }
bable转化语法
webpack 默认情况下,只能打包处理 JS 这一类的静态资源 但是,如果我们的项目代码中用到了 ES6 的新语法,此时,为了代码能够在常见的浏览器中 正常运行,应该将 ES6 的语法转化为 ES5 语法,然后,再运行在浏览器中。 所以,需要打包处理 ES6 的语法。 webpack 自身只认识所有的JS模块化语法,但是,它无法将 ES6代码转化为ES5代码。 如果需要将 ES6 转化为 ES5 语法,应该使用: babel 来处理。 babel的作用: 1 将 ES6 语法转化为 ES5 的语法 今天就开始使用下一代 javascript 语法。( 拥抱未来 ) 比如: const fn = a => 1; 转化为:var fn = function(a) { return 1; } 2 实现浏览器兼容,也就是说:如果浏览器不兼容某个方法,那么,babel会自动的模拟这个方法的功能来自己实现一份。然后,在这个浏览器中就可以使用这个功能了 比如: String.prototype.padStart var str = ‘abc‘; str.padStart(); babel的使用步骤: 1 安装: npm i -D babel-core [email protected] npm i -D babel-preset-env 2 在 webpack.config.js 中添加 打包JS 文件规则 3 在项目根目录中创建一个 babel 配置文件: .babelrc babel-preset-env 表示用来解析所有的标准JS语法,包括了: ES2015/ES2016/ES2017/ES2018... JS语法(ECMAScript),每年都会发布一版,可以使用 年份 来作为版本号,或者使用 数字版本号来表示 比如: ES6 也就是 ES2015 ES7 -> ES2016 ES8 -> ES2017 ES9 -> ES2018 一般平时所说的 ES6 就表示所有的新的JS语法 除了标准的JS语法之外,还有一些不标准但是经常会使用的语法,此时,推荐使用 babel-preset-stage-2 这个包,来处理 安装: npm i -D babel-preset-stage-2 JS语法提案,需要经过 5 个阶段,才会被采纳为正式标准 http://es6.ruanyifeng.com/#docs/intro
20、Vue中两种不同的编译模式
1、完整版:相当于 运行时 + 编译器
? 编译器:用来将模版字符串编译成为JavaScript渲染的代码,可以理解为解析Vue实例中的template模版内容,也就是说:只有编译器才能解析vue实例中的template配置项
? vue-cli 脚手架中导入的vue是完整版,因此,才可以解析template配置项
2、运行时版:基本上就是除去编译器的其他一切
? 注意:因为运行是版本没有编译器,所以,如果用到了运行时版本,是无法解析vue实例中的template配置项的,而我们自己配置的webpack中,默认导入vue是只包含运行时版本的!所有,我们自己写的代码中,只能使用render方法来渲染
在通过import vue from ‘vue‘导入vue的时候,默认导入的是:运行时版本,如果要导入完整版的vue,需要在webpack中添加一个 配置项才可以
21、Vue中的单文件组件
以.vue 作为后缀名的文件,就是vue中的单文件组件
为什么要使用单文件组件?
1、原来我们使用全局组件或者局部组件在使用模版的时候,是纯字符串,无法得到代码提示,没有语法高亮
2、一个组件是由html 、js 、css 样式组成的,但是这三部分被分离了
3、 原来的方式,限制只能使用 HTML 和 ES5 JavaScript, 而不能使用预处理器,
使用单文件组件的步骤:
1、安装:npm i -D vue-loader vue-template-compiler
2、在webpack.config.js 中添加loader 规则的配置 以及一个插件
// 打包处理 .vue 文件 配置的规则 { test: /.vue$/, use: ‘vue-loader‘ } 插件 // 这个包用来辅助解析 .vue 文件 const VueLoaderPlugin = require(‘vue-loader/lib/plugin‘)
3、创建单文件组件
4、创建vue实例,在使用单文件组件
4.1 安装: npm i vue 4.2 在 main.js 中导入 vue 4.3 创建 Vue 实例 4.4 渲染 App.vue 单文件组件
// 4.2 在 main.js 中导入 vue // const Vue = require(‘vue‘) // ES6中的 模块化语法规范!!! import Vue from ‘vue‘ // 导入 单文件组件 // const App = require(‘../App.vue‘) import App from ‘../App.vue‘ // 4.3 创建 Vue 实例 const vm = new Vue({ el: ‘#app‘, data: {}, // 4.4 渲染 App.vue 单文件组件 render: function(createElement) { return createElement(App) } })
22、vue-cli vue的脚手架的使用
因为 webpack 功能很强大,是前端必不可少的打包工具,但是,因为 webpack 配置太繁琐、太复杂了,这样,会导致拦住了一些想用Vue但是不会用webpack开发人员。为了解决这个问题,Vue作者就提供了一个脚手架工具,我们通过这个工具,只要一条命令,就可以快速生成一个配置好webpack的Vue项目了。这样,我们就不需要自己手动配置webpack,直接使用即可
-
1 全局安装: npm i -g vue-cli
-
2 使用命令,快速生成一个Vue项目:
vue init webpack 项目名称
-
3 进入项目:
cd 项目名称
-
4 启动项目:
npm run dev
vue-cli 脚手架工具
vue-cli 项目配置
/*
? Project name (vue-cli-demo) 项目名称
?
? Project description (A Vue.js project) 项目描述
?
? Author (zqran <[email protected]>) 作者
?
? Vue build (Use arrow keys) 构建模式
> Runtime + Compiler: recommended for most users
Runtime-only: about 6KB lighter min+gzip, but templates (or any Vue-specific HTML) are ONLY allowed in .vue files - render functions are required elsewhere
? Install vue-router? (Y/n) 是否安装路由,Y 是, n 否
?
? Use ESLint to lint your code? (Y/n) 语法检查工具,校验代码是否规范
? Pick an ESLint preset (Use arrow keys)
> Standard (https://github.com/standard/standard) 不带分号
Airbnb (https://github.com/airbnb/javascript) 更加严格,规范写代码的方式
none (configure it yourself)
?
? Set up unit tests (Y/n) 单元测试,选择 n
? Setup e2e tests with Nightwatch? (Y/n) 端到端测试,选 n
?
? Should we run `npm install` for you after the project has been created? (recommended) (Use arrow keys) 是否立即下载依赖项
> Yes, use NPM
Yes, use Yarn
No, I will handle that myself
*/
脚手架生成项目结构说明
├── build/ # webpack 配置├── config/ # webpack 配置├── node_module/ # 项目中安装的依赖模块├── src/ # 写代码的目录│ ├── main.js # 程序入口文件│ ├── App.vue # 根组件│ ├── router/ # 路由文件│ ├── components/ # 其他组件│ └── assets/ # 资源文件夹,一般放一些资源文件,比如:css/图片等├── static/ # 纯静态资源,不需要webpack处理 (直接拷贝到dist/static/里面)├── .babelrc # babel 配置文件├── .editorconfig # 编辑配置文件├── .eslintignore # eslint忽略文件├── .eslintrc.js # eslint配置文件├── .gitignore # git配置文件├── .postcssrc.js # postcss配置文件├── index.html # index.html 入口模板文件└── package.json # 项目文件,记载着一些命令和依赖还有简要的项目描述信息└── 9.md # 介绍自己这个项目的,可参照github上star多的项目。
Vue项目说明
学什么?
-
1 如何使用 Vue 开发一个项目
-
2 ElementUI 组件库的使用
-
3 业务逻辑(登录、权限、商品)
创建项目
-
通过 vue-cli 脚手架工具直接生成一个项目
-
1 创建:
vue init webpack shop-admin
-
2 配置(根据脚手架的引导来选择配置项)
-
3
cd shop-admin
-
4 启动项目:
npm run dev
或npm start
手动配置路由(要自己下载 vue-router)
-
1 安装:
npm i vue-router
-
2 在
src
目录中,新建router
文件夹,在文件中新建index.js
路由的配置文件 -
3 在
index.js
中,配置路由,并导出路由实例 -
4 在
main.js
中导入 路由配置,并且与 Vue实例 关联到一起 -
5 在
App.vue
中,添加路由出口
element-ui 组件库的使用
-
1 安装:
npm i element-ui
-
2 在 main.js 中,导入 element-ui、样式,并安装插件(在文档指南中,有element-ui的样式说明)
-
3 直接在文档中找到对应的组件使用即可
项目接口本地搭建
-
0 解压
shop-api.zip
文件 -
2 打开
navicat for mysql
工具,连接数据库 -
3 在链接上,点右键新建数据库,数据库名称为:
shop_admin
-
4 双击启用数据库,再右键
运行SQL文件
,选中 shop_admin.sql,并导入 -
5 打开
shop-api
目录,修改config/default.json
中的数据库用户名和密码(如果需要) -
6 在
shop-api
目录中,打开终端,输入:npm start
启动接口服务
以后每天写项目之前要做的事情
-
1 开启 phpStudy 中的 MySql 服务
-
2 在
shop-api
目录中,执行npm start
启动接口服务
使用 git 管理项目
# 原始提交到远程仓库的命令
git push https://gitee.com/zqran/shop_admin_31.git master
# 有了 remote 后:
git push shop master
# 使用 -u 命令后:
git push shop
?
# 将 仓库地址 与 shop 这个名称关联到一起
git remote add shop https://gitee.com/zqran/shop_admin_31.git
?
# -u 就是将 master 设置为默认,提交的时候,就不需要每次都指定 master 分支了
# -u 命令只需要执行一次即可
git push -u shop master
浏览器中的模块化规范:AMD(require.js)
NodeJS中的模块化规范:CommonJS( 模块化规范是所有规范中的一个内容,其实还 包含了很多其他的规范 ) 这两个规范,都是由 社区 提出,并且被前端开发者接收使用的,但并不是标准!!! 而 ES6 中的模块化规范,才是真正的前端模块化标准,因为,这是从ECMAScript语言规范提出的
在模块化规范中,每个JS文件都是一个独立的模块
ES6中的模块化语法: 1 导入模块
? import
2 导出模块
? export
第一种导入导出语法:
导出:export default str default 导出在一个模块中只能出现一次 default 导出的内容,在导入的时候,名称可以是任意的(也就是 a 可以是任意名称) 导入:import a from ‘./a‘
第二种导入导出语法:
导出: export const num = 1 export(非default导出)可以使用任意多次 export 导出的内容,在导入的时候需要使用 {},并且导出的名称必须与导入的名称完全相同 导入: import { num } from ‘./b‘
如果模块中导出的内容 与 当前模块中的变量命名冲突,此时,可以通过 as 语法,来起别名,解决 命名冲突的问题
const num = 666 // 导入b模块内容 // // import { num as num1 } from ‘./b‘ // console.log(‘b模块导出内容为:‘, num1, num) // 一次性导入 b 模块中的所有内容 import * as b from ‘./b‘ console.log(b)
登录访问控制
1、如果没有登录,只能访问登录,而不能访问其他页面
2、如果没有登录,访问其他页面,应该跳回到登录页面
3、如果登录了,就可以访问其他页面
如何知道有没有登录
1、vue项目中使用‘token‘ 来作为登录成功的标识
只要有token就说明已经登录了
如果没有token就说明还没有登录
2、原来是通过sessionid + cookie机制,来实现登录状态保持的
将sessionid存储到cookie中,这样,登录状态标识(sessionid),就会随着每一次请求都发送给服务器,服务器从cookie中获取sessionid,就知道是否登录过了
这两种不同的机制都是为了解决同一个问题:登录状态保持
为什么需要状态保持? 因为http协议是无状态的
现在登录成功后,需要将token保存起来,存储到localStorage中,其他地方用到了,就直接从loaclStroage 中取出来使用就可以了
补充:token也是有时间限制的,会失效的比如可以设置半个小时;在浏览器中可以设置顺延,比如打开一个页面,用户有事出去了,没有点击其他内容,会顺延一个时间,不会直接退出(一般而言)
ref说明 , $refs 是vue提供的一个对象
// this.$refs[formName] 就是在访问对象 $refs 中的属性 // this.$refs.loginForm 通过 .语法来访问对象属性,与上面通过 []
作用: 用来获取页面中所有带有ref属性的元素(DOM或组件)
作用:添加给 DOM元素或组件,将来可以通过 this.$refs.ref的值
来获取到这个DOM对象或组件。然后,就可以进行DOM操作或获取组件数据、调用组件方法
为什么要获取DOM对象或者组件对象?
因为在vue中,有些情况下,需要手动操作Dom,此时可以通过ref来获Dom对象,然后,在进行dom操作
如果给组件添加了ref属性,那么可以通过ref来获取到组件对象
在当前案例中,就是通过 $refs.loginForm 来获取到表单组件,调用组件中的 validate 方法,来进行表单验证 通过 $refs 获取到组件对象,并且调用组件的 validate 方法,进行表单验证 this.$refs[formName].validate(valid => { // valid 形参表示:表单验证是否成功 if (!valid) { // 验证失败的时候,代码中不需要任何处理,因为,错误信息都已经在页面中展示给用户了 return false }
编程式导航--js代码实现路由跳转功能
this.$router.push(‘/home‘) 字符串的形式 参数 /home:表示要跳转到的路由地址 this.$router.push({ name: ‘home‘ }) 可以是一个对象 通过 name 属性来实现路由跳转
导航守卫
全局导航守卫:任何一个路由的切换,都会经过全局导航守卫
router.beforeEach((to,from,next)=>{ to 到哪去,也就是导航到的路由 from从哪里来,也就是从哪个路由切换到了当前路由 next() 方法是放行的信号,必须调用这个方法,那么,组件的内容才会展示在页面中 其他使用方式: next(‘/login‘) 参数 /login 就表示要跳转到的路由path,也就相当于:跳转路由 next(‘/login‘) 注意使用next(‘./xx)跳转到其他页面的时候,一定要开启next(),不然会一直循环,直到保存,会运行一千多次后报错 })
props类型的说明
:router = ‘true‘ 此时,router 的值是布尔值类型的
设置为字符串类型: router = "true"
设置为数值类型:router="true"
此时,router的值是字符串类型的
嵌套路由使用步骤
1、在创建路由的规则的时候,要配置子路由的规则,而不是普通的路由;子路由是通过路由中的children属性来配置的
2、在需要切换内容的组件中,添加一个子路由的出口 <router -view>
Promise
axios 是基于promise的,实际上axios.get()返回的就是一个promise对象;.then()方法就是promise提供的
promise 是js中实现异步编程的一个解决方案。
在没有promise之前,我们是通过回调函数,来实现异步编程的
js是单线程,只能同时做一件事
使用回调函数 实现js异步编程的问题:回调地狱
$.ajax({ url: ‘‘, success: function (data) { // 在这个回调函数中,就可以通过 data 异步获取到服务器返回的数据 $.ajax({ url: ‘‘, success: function (data) { // 在这个回调函数中,就可以通过 data 异步获取到服务器返回的数据 $.ajax({ url: ‘‘, success: function (data) { // 在这个回调函数中,就可以通过 data 异步获取到服务器返回的数据 } }) } }) } }) // 这两种定时器中也是通过 回调函数 机制,来实现“异步效果”的 setTimeout(function() {}, 0) setInterval(function() {}, 0)
1、回调嵌套回调,看起来很丑(可读性差)
2、异步体现不直观(代码执行顺序与正常思维顺序不一致)
3、在解决复杂异步问题时,就变得很棘手
promise使用的好处
1、 可读性更好,不用层层回调嵌套了
2 、看起来更加直观,因为:使用 Promise 就是以一种同步的方式来编写异步代码
3、 很容易解决复杂的异步问题
promise的使用:
1、promise是一个构造函数
2、可以将promise看作一个容器,内部封装了异步操作
3、promise的参数是一个回调函数
4、回到函数有两个参数:
? resolve 表示成功的回调函数,也就是:当异步操作执行成功时,应该调用这个函数
reject 表示失败的回调函数,也就是,当异步操作执行失败时,应该调用这个函数
异步操作成功时,调用then中的回调函数 then表示成功,当异步操作成功时,这个回调函数就会调用 理解:Promise内部调用的 resolve 相当于就是调用了then中的回调函数 并且,如果 resolve 调用的时候传递了参数,还可以通过 then 中的回调函数来获取到这个参数 const p = new Promsie((resolve,reject)=>{ resolve() }) p.then(data=>{ console.log(‘成功了‘,data)}) .then(data=>{console.log(data)}) 返回值,可以作为下一个then回调函数的参数 异步操作失败时,调用catch中的回调函数 异步操作失败时,会调用 catch 中的回调函数 注意:错误被 catch 捕获后,后面的 catch 就不会再执行了 第一种写法 const p = new Promise((resolve,reject) =>{reject()}) p.catch(err=>{console.log(‘出错了‘)}).catch(()=>{console.log(‘异步出错了‘)}) 第二种写法 可以指定第二个回调函数,当异步操作失败了,第二个回调函数的代码就会执行 p.then(()=>{console.log(‘成功了‘)},err=>{console.log(‘异步操作失败了‘)})
promise的状态
每一个promise对象,内部都有三个状态
1 pending 等待中
2 resolved 成功
3 rejected 失败
状态转换 pendinig ---> resolved 异步操作成功
? pending ---》 rejected 异步操作失败
状态只能改变一次,也就是说,只要状态发送改变了,就不会再次发生改变
promise中几个常用的方法
需求:有三个异步请求,等到这三个异步请求都完成的时候,再执行某个操作
promise.all([ p,p1,p2]).then(res => { })
需求:有三个异步请求,等到其中一个异步请求完成,就执行某个操作
promise.race([ p,p1,p2]).then(res => { })
异步编程终极解决方案: async 和 await
这两个关键字用来实现异步编程,是依赖于promise的
async 异步
await 等待
注意: await 只能用在async 函数中
添加async的规则是:就近原则,一定要加在使用await关键字的函数中
async就是用来修饰这个函数的,也就是告诉js解析引擎这个函数包含了await 这样的关键字处理的异步操作
await会等到后面的异步操作完成,异步操作完成后,后面的代码才会执行
await后面可以跟任意的表达式,但是一般都会跟着一个promise对象,也就是一个异步操作
错误处理
try{ } catch (e){ }
vue中的插槽 slot
vue中的插槽(slot):可以在封装组件时使用组件标签中的子节点
默认情况下,组件的子节点是不会出现在页面中
什么情况下会使用插槽?
封装组件,为了增加组件的复用性
可以通过vue中的插槽来使用组件子节点
1、在组件中需要动态变化的地方,添加slot组件(占位置)
2、将要传递的内容作为组件标签的子节点
3、slot组件,就会被替换为组件标签的子节点
默认slot组件只能使用一次!如果使用多次,就会报错
没有name属性的插槽,叫做:默认插槽
具名插槽
给slot起一个名称,那么,在一个组件中,就可以使用多个slot了
1、在组件中,使用slot的时候,添加name属性
2、在给组件标签添加子节点的时候,质地你哥slot=“插槽的name”
3、这样的,对应的内容就会显示在对应的插槽
作用域插槽
当使用插槽的时候,组件标签的子节点内容是要展示在组件中的,也就是说:这些子节点最终是在组件内部使用的,所以,这些子节点中,应该能够使用组件内部的数据
但是默认情况下,组件标签子节点中无法直接使用组件内部的数据!!!
为什么
因为作用域(能否在组件中访问到某个数据)问题
标签在哪个组件的模版中使用的,那么,它就只能访问到当前组件的数据
比如: msg-box标签的子节点p标签,是写在vue实例的模版中的,所以,p标签中只能使用vue实例中的数据
那么,我们就说p标签所在的作用域就是vue实例
我们希望,在组件模版的外部,直接获取组件内部的数据,也就是让:
p标签 的作用域变为msg-box组件,为了达到这个效果,应该使用作用域插槽来实现!
作用域插槽:
1、在组件模版中的slot标签中,添加要被使用的数据作为属性
2、在p标签(组件外部的标签)中,添加slot-scope=‘scope’ slot-scope 是固定的属性名称值
scope 可以是任意的名称,表示要暴露出来的数据的集合(对象)
优化axios的使用
1、每次发送请求的时候,都要拷贝完整的基准地址
期望:配置基准地址,在发送请求的时候,只需要填写当前接口地址
配置基准路径:
axios.defaults.baseURL = ‘http://www.baidu.com‘
2、每次发送请求的时候,都需要设置一次请求头
期望:配置请求头,在发送请求的时候,就不需要每次都单独设置请求头
请求拦截器
说明:因为只要是axios发送的请求,都会执行请求拦截器中的代码,所以,可以在请求拦截器中,一次性添加请求头
axios.interceptors.request.use(config => { // 统一添加 Authorization 请求头 config.headers.Authorization = localStorage.getItem(‘token‘) // 一定要返回 config return config }))
3、每个组件中都要单独导入axios
期望:导入一次,而不是每个组件中都要单独导入
import axios from ‘axios‘ Vue.prototype.$http = axios
vue 中的template简单说明
1、它是vue中内置的 组件,不需要我们注册这个组件,直接使用即可
2、是一个占位符(幽灵节点),不会出现在页面中
作用:包裹多个html元素,template仅仅是一个占位符,最终渲染在页面中的时候,template不会出现在页面中,只有template中的html元素,会显示在页面中
vue中的.sync修饰符
1、 sync 单词:同步 2、 作用:通过添加这个修饰符,可以实现在组件内部直接修改props 3 、props 是只读的,子组件中无法直接修改 props 值的!!! 4 、原理:通过子到父组件通讯机制来实现了 子组件 修改 父组件中的传递过来的 props 功能
ES6中的解构
对象的解构 和数组是结构示例
对象的演示 const obj = { name :‘jack‘,age :19} let {name,age} =obj console.log(name,age) 解构的同时起别名 age1 就是一个别名,有了别名后,原来的名称 age 就无效了 const { name, age: age1 } = obj console.log(age1, name) 演示解构复杂对象 解构出 obj.score.math const { score: { math } } = obj console.log(math)
数组是演示 const arr = [1,2,3,] 获取所有 const [a,b,c] =arr 只获取第二个,第三个参数 const [,b,c] = arr ...b表示获取所有剩余的数据,并且是一个数组 const [a, ...b] = arr
vue中组件样式的问题说明
注意:组件中的样式应该只对当前组件生效,而不是要对其他组件产生影响!
如果产生影响,产生组件之间样式冲突、覆盖等问题
vue中提供了一个scoped,只要给组件的style标签添加这个属性,那么样式就只会对当前组件生效,而不会影响其他组件,所以推荐使用组件的时候,给style标签添加scoped属性
scoped 原理:动态给组件中的标签添加一个data-v-组件唯一标识属性,并且样式中自动添加了属性选择器,因为每个组件的唯一标识是不一样的,所以,样式就只会对当前组件生效了
ES6中是数组扩展运算符和对象扩展运算符
数组 const arr = [‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘, ‘g‘] 打印数组中的每一项: console.log(arr[0], arr[1], arr[2....) 使用 数组扩展运算符 : ... 就是数组扩展运算符,作用就是:将数组中的每一项内容分别取出来 console.log(...arr) 合并数组,将arr1 和 arr 合并到一起 const arr1 = [1, 2, 3] 相当于:[‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘, ‘g‘, 1, 2, 3] const newArr = [...arr, ...arr1]
对象的扩展运算符: 原来 对象的扩展运算符 不是一个标准的JS语法,因此,使用的时候,需要 babel-preset-stage-2 来解析 现在,ES2018 将这个运算符引入了对象 const obj = { name: ‘jack‘ } const obj1 = { age: 19, name: ‘rose‘ } // 先将对象 obj 中的属性取出来放在新对象中 // 再将对象 obj1 中的属性取出来放在新对象中 // 相当于 浅拷贝 // 如果有重名的属性,以后面的属性为准 const newObj = { ...obj, ...obj1 } console.log(newObj)
项目完成之后的操作,打包处理等
Vue项目如何打包?
npm run build 打包后的内容,会被放到 dist 目录中 上线的时候,就是将 dist 文件夹中的内容,部署到服务器中就可以了 打包后生成文件,应该放在服务器中运行,而不能直接双击页面打开
默认情况下打包,打包出来的文件体积非常大!!!
问题: 1、 文件体积大,用户访问网站时加载速度慢,等待网站加载时间长 2 、访问网站的时候,一次性加载所有的资源文件(JS、CSS),这造成了浪费。因为用户可能只会用到项目中的某一些功能,所以,没必要一访问网站就将所有的内容,全部加载
项目打包的优化:
1 优化打包后的文件体积 解决方式:CDN 打包后,实际上就是 vendor 这个文件的体积非常大 vendor 一般用来表示:第三方文件(vue、vue-router、axios、element-ui、element-tree-grid、vue-quill-editor) 处理思路: 不再将第三方文件直接打包到 vendor.js 中,而是引用在线的地址来加载这些第三方文件,这样就可以解决 vendor 体积太大的问题了 2 按需加载 首屏加载时间是衡量网站性能的一个很重要的指标。 如何提高首屏加载速度? 只加载首屏中用到的内容,而首屏没有用到的,就不加载。等到用户看到这些内容的时候在加载 在 Vue 项目中,使用按需加载后,当访问某个路由的时候,才会加载这个路由对应的组件的内容 原理:利用 webpack代码分割 和 Vue异步组件 ,很容易就可以实现按需加载功能
优化:按需加载的步骤
说明:基于webpack 的代码分离和vue异步组件
1、修改router文件夹下index.js中导入的语法
把 import Home from ‘@/components/home/Home‘ 修改为: const Home = () =>import(‘@/components/home/Home‘)
把组件按组分块
有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用 命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4)。
const Foo = () => import(/* webpackChunkName: "group-foo" */ ‘./Foo.vue‘) const Bar = () => import(/* webpackChunkName: "group-foo" */ ‘./Bar.vue‘) const Baz = () => import(/* webpackChunkName: "group-foo" */ ‘./Baz.vue‘)
Webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。
2、 (该步可省略)修改 /build/webpack.prod.conf.js
中的 chunkFilename
{ // [name] 代替 [id] chunkFilename: utils.assetsPath(‘js/[name].[chunkhash].js‘) }
CDN简介
CDN(Content Delivery Network):内容分发网络 对于常用的资源(比如:Vue/vue-router/axios 等)这些都是有免费的 CDN 服务的 所以,对于这些文件,我们项目中直接用就可以了。 但是,对于我们公司中自己写的JS/CSS/图片等文件,就没有免费的CDN使用了。 这时候,如果要想使用CDN提供的优势,就需要公司自己花钱买CDN服务。 什么内容需要被CDN缓存? JS、CSS、图片等前端资源 但是对于 接口请求 不能被缓存!!! 比如,有一张图片已经被CDN缓存了,但是,现在图片更新了,此时,我们希望用户看到最新的图片,此时,如何解决缓存的问题??? 1 只要从 CDN 服务的后台,清理CDN缓存就行了,也就是说:你购买的CDN服务器后台,一般都会提供清CDN缓存功能的 2 只要让新文件的路径与旧文件的路径不同即可 比如: a.jpg -> b.jpg a.jpg -> a.jpg?t=151231231213 添加时间戳(或者随机数)查询字符串,就可以解决浏览器缓存的问题
优化:使用CDN
项目中使用CDN
问题:打包后 vendor
(第三方资源文件) 体积过大
-
原因:项目中用到了很多的第三方包,这些包在打包的过程中,全部打包到了
vendor
这个一个JS文件中,导致体积过大 -
如何优化
vendor
体积? 使用CDN -
原理:使用CDN处理第三方包,是引用的在线地址。此时,这些包不会被打包到
vendor
文件中,因此,vendor
体积就会变的更小
CDN使用步骤
-
1 在
index.html
中引入 CDN 提供的 JS 文件 -
2 在
/build/webpack.base.conf.js
中(resolve 前面)添加配置 externals -
注意:通过 CDN 引入 element-ui 的样式文件后,就不需要在 main.js 中导入 element-ui 的 CSS 文件了。所以,直接注释掉 main.js 中的导入 element-ui 样式即可
externals 配置 externals: { // 键:表示 导入包语法 from 后面跟着的名称 // 值:表示 script 引入JS文件时,在全局环境中的变量名称 // window.Vue / window.axios / window.VueRouter vue: ‘Vue‘, axios: ‘axios‘, ‘vue-router‘: ‘VueRouter‘, // 注意:带有样式文件的第三方包,需要在 代码中 将样式注释掉!!! ‘element-ui‘: ‘ELEMENT‘, ‘element-tree-grid‘: ‘ElTableTreeColumn‘, ‘vue-quill-editor‘: ‘VueQuillEditor‘ BMap: ‘BMap‘, echarts: ‘echarts‘, }
常用包 CDN
-
说明:
-
1 先在官方文档查找提供的 CDN
-
2 如果没有,在
https://www.bootcdn.cn/
或其他 CDN 提供商 查找
?
-
缓存和保留组件状态
解决方式:使用 keep-alive
,步骤如下:
1、在需要被缓存组件的路由中添加meta属性 meta属性用来给路由添加一些元信息(也就是一些附加信息) { path:‘/‘, name:‘home‘, component:Home, 需要被缓存 meta:{keepAlive:true} } 2、修改路由出口,替换成以下形式,根据meta是否只有keepAlive属性,决定该路由是否被缓存 <keep-alive> <!-- 这里是会被缓存的视图组件 --> <router-view v-if="$route.meta.keepAlive"> </router-view> </keep-alive> <!-- 这里是不被缓存的视图组件 --> <router-view v-if="!$route.meta.keepAlive"> </router-view>
启动路由的History模式
通过在路由中添加mode:history 可以去掉 浏览器地址栏中的#
在开发期间,只需要添加这个配置即可
但是,在项目打包以后,就会出现问题 ,需要后端配合解决
后端的处理方式
-
1 优先处理静态资源
-
2 对于非静态资源的请求,全部统一处理返回 index.html
-
3 当浏览器打开 index.html 就会加载 路由的 js 文件,那么路由就会解析 URL 中的 /login 这种去掉#的路径了要后端配合解决
?
反向代理
-
修改
config/index.js
配置文件
proxyTable: { // 使用:/api/movie/in_theaters // 访问 ‘/api/movie/in_theaters’ ==> ‘https://api.douban.com/v2/movie/in_theaters‘ ‘/api‘: { // 代理的目标服务器地址 target: ‘https://api.douban.com/v2‘, // https请求需要该设置 secure: false, // 必须设置该项 changeOrigin: true, // ‘/api/movie/in_theaters‘ 路径重写为:‘/movie/in_theaters‘ pathRewrite: {"^/api" : ""} } }
代理服务器原理:
1、目标服务器的地址
2、接口规则,让代理服务器知道哪些请求需要被代理
Vuex是专门为vue提供的状态管理工具
状态即数据
状态管理就是管理vue组件中的数据
内部机制:vuex采用集中式的方式,来统一管理vue中需要共享的数据(即多个组件中都会用到的数据)
vuex中的数据也是响应式的
vuex借鉴了Flux、Redux等状态管理模式,前端最早的状态管理思想就是React中的Flux
有什么用?
使用场景:复杂项目中,解决组件通讯的难题
什么时候用?
1、不要觉得vuex很强大就使用,这是个错误的观点
2、小项目不需要使用vuex
3、只有大型项目或者开发过程中觉得组件通讯太多,管理组件外的数据太麻烦,此时可以使用vuex
Vuex中的核心概念
store
-
1 一个Vue项目中只能有一个store(仓库)
-
2 store中提供了:1 共享的数据(state) 2 操作数据的方法
-
3 如果想要修改 store 中的state,应该通过 store 提供的方式,而不能手动修改!!!
state
-
状态即数据
-
state 也就是原来 Vue 中的 data
-
一个Vue项目中应该只有一个state
getters
-
Vuex 的计算属性
-
使用方式 与 计算属性一样
-
创建的时候是一个方法
-
使用的时候是一个属性
$store.getters.leftCount
-
mutations
-
作用:提供修改 state 的方法
-
说明:数据只能通过 mutations 中提供的方法来修改数据
-
注意:mutations中只能同步修改数据!!!
actions
-
作用:封装异步操作,调用 mutations 修改数据
-
区分 同步和异步 最本质的区别: 异步操作放在 actions 中能用 devtools 追踪状态变化;如果放在 mutations 中是无法追踪状态变化的
-
以上是关于vue总结的主要内容,如果未能解决你的问题,请参考以下文章