内容很多,全是干货,建议仔细观看
第一章 搭建本地服务器
1.1 webpack-dev-server搭建本地服务器
注意:
dev: webpack-dev-server --open
这个代码后面的--open
意思是服务器启动之后,自动打开index.html
- 这个插件的版本最好和webpack的版本对应
- 丑化插件开发的时候先不用,打包的时候再用
1.2 webpack配置文件的分离
有些时候,我们总有开发的时候要用到的配置,但是发布却用不上,也有发布的时候要用,但是开发的时候就不用, 例如: 开发的时候就不用压缩js的插件
我们来看看怎么做
-
安装
webpack-merge --save-dev
-
创建三个js配置文件,一个叫做
base.config.js
,一个叫做prop.config.js
,一个叫做dev.config.js
-
第一个是公共样式文件,无论是开发的时候还是运行的时候,都要用到
-
const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/main.js', output: { //这里要写绝对路径,我们这里最好获取动态的路径 //下面的这个__dirname是node的东西,它代表项目的根目录的绝对路径 path: path.resolve(__dirname, '../dist'), filename: 'bundle.js', //publicPath: 'dist/' }, module: { rules: [ { test: /\.css$/i, //是用loader的时候,是从右向左读取的 use: ['style-loader', 'css-loader'], }, { test: /\.less$/i, loader: [ // compiles Less to CSS "style-loader", "css-loader", "less-loader", ], }, { test: /\.(png|jpg|gif|jpeg)$/i, use: [ { loader: 'url-loader', options: { //当图片大小小于limit的时候,会将图片编码为base64格式,如果大于,那么就保留图片原先的格式,但是我们需要使用file-loader来加载这个图片 limit: 8192, //我们引用的时候不用担心这里会引发找不到图片路径的问题 name: 'img/[name].[hash:8].[ext]' }, }, ], }, { test: /\.m?js$/, //这句话的意思就是排除这两个文件夹 exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }, { test: /\.vue$/, use: ['vue-loader'] } ], }, resolve: { alias: { //vue指向一个具体的文件夹 'vue$': 'vue/dist/vue.esm.js' } }, plugins: [ new webpack.BannerPlugin('最终版权归CJY所有'), new HtmlWebpackPlugin({ template: 'index.html' }) ] }
-
-
第二个是编译成发布的时候要用到的配置文件,里面有一些打包的时候才要用到的插件例如:
uglifyjs-webpack-plugin
-
const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin'); const webpackMerge = require('webpack-merge'); const baseConfig = require('./base.config'); module.exports = webpackMerge(baseConfig,{ plugins: [ new uglifyjsWebpackPlugin() ], })
-
-
第三个是开发的时候要用到的配置文件,里面有一些开发的时候才要用到的配置, 例如:
webpack-dev-server
-
const webpackMerge = require('webpack-merge'); const baseConfig = require('./base.config'); module.exports = webpackMerge(baseConfig,{ devServer: { contentBase: './dist', //表示是否需要实时监听 inline: true } })
-
-
创建的位置如图
-
-
package.json
配置文件里面的脚本命令要修改-
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config ./build/prod.config.js", "dev": "webpack-dev-server --open --config ./build/dev.config.js" } //如果不修改的话,默认引用的地方就是根目录的webpack.config.js
-
-
同时,我们要注意,dist打包的地方原来是
path: path.resolve(__dirname, 'dist')
,这样配置的话,dist就打包到,./build/dist
里面去了,所以我们要修改这里为path: path.resolve(__dirname, '../dist')
-
运行就可以了
第二章 vue-cli
2.1 概述
什么是cli?
- CLI是
Command-Line Interface
, 翻译为命令行页面,但是俗称脚手架 - Vue CLI是一个官方发布vue.js项目脚手架
- 使用vue-cli可以快速搭建Vue开发环境以及对应的Webpack配置
使用node的前提–node,webpack
2.2 脚手架的安装
安装脚手架:
npm install @vue/cli -g
- 上面的安装方式对应的是安装最新版本的cli,但是如果我们想用cli2的版本,那么我们就要拉取cli2的模板
拉取 2.x 模板 (旧版本)
-
Vue CLI >= 3 和旧版使用了相同的
vue
命令,所以 Vue CLI 2 (vue-cli
) 被覆盖了。如果你仍然需要使用旧版本的vue init
功能,你可以全局安装一个桥接工具:npm install -g @vue/cli -init # `vue init` 的运行效果将会跟 `vue-cli@2.x` 相同 vue init webpack my-project
VueCLI2初始化项目
vue init webpack my-project
VueCLI3 初始化项目
vue create my-project
2.3 runtime-compiler和runtime-only的区别
我们来看看Vue运行的的过程
可以看到,模板都是要经过template -> ast -> render -> vdom -> UI 这个过程
但是,事实真是如此吗?
我们观察用runtime-compiler
创建的项目和用runtime-only
创建的项目在main.js
文件上面有什么区别
左边的是compiler, 我们可以发现,左边引用的是模板,但是右边确实一个render函数
因此,我们可以知道
- 左边的写法在运行的时候,要经过
template -> ast -> render -> vdom -> UI
这么多步骤 - 右边的可以直接
render -> vdom -> UI
就可以了
我们还可以知道
runtime-only
的写法效率高runtime-only
的源码的量比runtime-compiler
少6kb
**为什么? **
- 因为我们有
"vue-template-compiler": "^2.5.2",
这个plugin,他的作用是: 在.vue
文件编译的时候,会自动将他转化为一个obj对象, 这个对象里面有render函数,因此我们在别的js文件中调用.vue
文件,引入的其实是一个对象,而这个对象早已经经过了template -> ast
这两个过程了,因此,这种写法可以直接只有三步就可以了 - 注意: 其实
runtime-compiler
这种创建方式也是完全可以用右边的方式来写的,因为runtime-compiler
创建的项目的vue的源代码包括了runtime-only
所有的源代码,因此左边的也可以用`
再来聊聊render函数里面的参数
-
我们可以知道,这个参数是一个函数,叫做
createElement()
,有两种用法-
普通用法:
createElement('标签名','{class名的键值对}',[标签里面的内容])
-
例子:
render: function (createElement) { return createElement('h2',{class: 'box'},['helloWorld' , createElement('button',['按钮'])]) }
-
-
特殊用法:
createElement(组件名)
-
例子:
render: h => h(App)
-
-
2.4 Vue-cli3
- vue-cli3 和 vue-cli2的区别
- 3是基于webpack4打造的,而2是基于webpack3
- 3的设计原则是“0配置”,移除根目录下的配置文件,build和config等文件
- 3提供了 vue-ui 命令,提供了可视化配置,更加人性化
- 移除了static文件夹,新增了public文件夹,并且index.html移动到了public中
目录结构
vue-cli3中的vue.js
-
我们会发现创建的vue实例变成了下面这种写法
-
new Vue({ render: h => h(App), }).$mount('#app')
-
-
他和cli2中下面这种写法是等价的(底层一致)
-
new Vue({ el: '#app', render: h => h(App) })
-
配置文件的查看与修改
相比于cli2,我们会发现,cli3中的很多配置信息,都被刻意的隐藏起来了,所以我们要学会怎样去查看和修改
UI方面的配置
-
启动配置服务器
vue ui
-
我们会进入一个项目的管理页面
-
导入我们的项目,然后我们就能看到如下页面
-
在配置中就能查看和修改我们的配置了
那我要是想要在文件里面修改配置呢?
-
在项目的根目录创建
vue.config.js
文件 -
在里面写上
modules.exports = { 要修改的配置 }
-
只要这样写,这些配置会自动和隐藏在
node_modules
文件夹里面的配置进行合并
第三章 v-router
3.1 路由概述
什么是路由?
- 路由是一个网络工程里面的术语
- 路由就是通过互联的网络把信息从源地址传输到目的地址的活动
路由提供了两种机制:路由和转送
- 路由是决定数据包从来源到目的地的路径
- 转送将输入端的数据转移到合适的输出端
路由中有一个非常重要的概念叫做路由表: 路由表本质上就是一个映射表,决定了数据包的指向
3.2 前端渲染,后端渲染和前端路由,后端路由
后端渲染
在早期的时候,页面一般都是用jsp/php来写的网页
- 早期的网站开发整个HTML页面是由服务器来渲染的
举例: (我们假设我们访问的网站用的是后端渲染,并且用的是jsp)
- 我们在浏览器访问
wwwl.taobao.com
- 服务器接收到浏览器发来的访问请求,并通过正则进行匹配,并且最后交给一个Controller进行处理
- 服务器发现浏览器要访问的是
www.taobao.com
,Controller开始在服务器中渲染页面 - 服务器中生成的页面包含
html+css+java
, java代码的作用就是从数据库中调取数据,并且将它动态的放在页面中,这完成了一个IO操作 - 这些都做好了之后, 服务器将渲染好的网页传回浏览器,注意: 这时候的网页已经是渲染好了的,同时:,传回的页面的构成是一个静态页面,只有html+css
- 如果用户访问了另外一个地址,那么就重复上述的过程
后端路由
- 后端处理URL和页面之间的映射关系
优点:
- 有利于SEO优化
缺点:
- 整个页面的模块都是后端人员来编写和维护的
- 另一种情况就是如果前端人员要开发,需要通过java或php来编写页面
- HTML代码和数据以及对应的逻辑混在一起,编写和维护都是非常糟糕的
前后端分离的阶段
- 后面随着Ajax的出现,出现了前后端分离的模式
- 后端只是提供API来返回数据,前端通过Ajax来获取数据,并且可以通过JS将数据渲染到页面中
优点:
- 前后端责任清晰,一个负责可视化,另一个负责数据
- 当在换一个端进行开发的时候,后端不用做任何处理,前端重新做就行了
很多网站现在依旧是这种模式开发
怎么做的?
- 用户输入网址
- 静态资源服务器解析你的地址,然后返回对应的
html+css+js
- 用户的浏览器拿到了这个三件套,然后执行js
- 若是js里面有api接口的请求,那么就去访问动态资源服务器
- 动态资源服务器,根据你的请求参数,请求什么接口,返回对应的数据
- 然后浏览器得到这些数据后,用于渲染啥的,最后网页就成型了
前端渲染
这种网页中大部分的内容,都是由前端写的js代码在浏览器中执行,最终渲染出的网页,叫做前端渲染
单页面富应用阶段(SPA)
- 其实SPA最主要的特点就是在前后端分离的基础之上加了一层前端路由
- 也就是前端来维护一套路由规则
它和前后端分离的阶段不同在哪里?
- 静态服务器中只有一套
html+css+js
,我们访问某个网址直接可以拿到所有的网址的页面 - 然后通过前端路由技术,用户访问不同页面的时候,就不用向静态服务器请求资源了(页面不刷新),页面直接对应的渲染,但是API的接口还是要重新请求的
前端路由
- 前端处理URL和页面之间的映射关系
核心: 改变URL,但是页面不进行整体的刷新
3.3 url和hash和HTML5的history
我们可以通过标题说的两个东西来让我们改变URL时,页面不进行整体的刷新
url和hash
可以运用locaiton.hash = "链接名"
来进行不刷新的连接跳转
例:
原本网址http://localhost:8080/#/
输入location.hash = 'dasd'
网址变成http://localhost:8080/#/dasd
注意:这样子改变的网址是可以前进和后退的
history的pushState
运用history.pushState({},'','/连接名')
可以改变url的地址而且不刷新页面
例:
原本的网址http://localhost:8080/#/
输入history.pushState({},'','/aaa')
网址变成http://localhost:8080/aaa
注意: 这里的改变网址之后,网址可以进行历史的前进和后退
history的replaceState
用法和上面完全一致,参数都一样
区别: 这里改变网址之后,不可以进行网址历史的前进和后退!!!
history的go,back和forward
这里就是控制网址历史的前进与后退
前端路由的实现
前端路由就是通过监听上述的改变url的方法,当url发生改变的时候,动态的加载(保存在本地的)要显示的页面,这样前端路由就实现了
3.4 认识vue-router
-
目前前端流行的三大框架,都有自己的路由实现:
- Angular的ngRouter
- React的ReactRouter
- Vue的vue-router
-
Vue-router是基于路由和组件的
- 路由用于设定访问路径,将路径和组件映射起来
- 在vue-router的单页面应用中,页面的路径的改变就是组件的切换
vue-router的安装与使用
-
安装:
npm install vue-router --save
-
在模块化工程里面使用它(因为他是一个插件,所以可以通过Vue.use()来安装路由功能)
- 导入路由对象,并且调用
Vue.use(VueRouter)
- 创建路由实例,并且传入路由映射配置
- 在Vue实例中挂载创建的路由实例
//我在这里配置所有的配置信息 //1 导入和vue使用这个插件 import VueRouter from 'vue-router'; import Vue from 'vue'; Vue.use(VueRouter); //2 创建路由的实例对象 const routes = [] const router = new VueRouter({ // 配置映射关系,这是一个数组,数组里面一个对象就是一个映射关系 routes }) //4 在main.js中引入这个实例,并挂载 export default router
- 导入路由对象,并且调用
3.5 路由映射配置和呈现出来的效果
使用vue-router的步骤
-
创建路由组件
-
//我在component文件夹里面创建了Home.vue和about.vue组件 //home组件 <template> <div> <h2>我是首页</h2> <p>hhh</p> </div> </template> <script> export default { name: "Home" } </script> <style scoped> </style> //about组件 <template> <div> <h2>我是关于</h2> <p>就这?</p> </div> </template> <script> export default { name: "about" } </script> <style scoped> </style>
-
-
配置路由映射: 组件和路径映射关系(在router文件夹里面的index.js里面的routes数组里面创建和配置组件)
-
//这里面有两个基本配置,一个就是虚拟路径,另一个就是我们要绑定的组件 const routes = [ { path: '/home', component: Home }, { path: '/about', component: about } ]
-
-
使用路由: 通过
<router-link>
和<router-view>
-
//在app.vue中实现这个路由 <template> <div id="app"> //下面这两个组件是全局组件,所以调用没有任何问题 //router-link标签在页面会被渲染为a标签 //路由切换的时候,切换的是router-view挂载的组件,其他内容不会发生任何改变 <router-link to="/home">首页</router-link> <router-link to="/about">关于</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App' } </script> <style> </style>
-
路由的默认值
上面的例子,我们会发现,当我们打开页面的时候,router-view
标签不会显示任何东西,但是我们想要刚打开就进入/home
的页面里面,这时候,我们就要进行一些设置
怎么做呢?
-
在router中的
index.js
的routes数组里面添加下面的句子就可以了-
{ path:'', //这是重定向 redirect: '/home' }
-
这段路由的意思就是, 当我们访问到空路径的时候,我们就会自动把路径重新定向到
/home
,也就是说,我们每次打开网页,都会去http://localhost:8080/#/home
而不是http://localhost:8080/
用history来改变uri
上面的例子,我们发现,每次访问路径的时候,前面都会带上#
号,例如:http://localhost:8080/#/home
这不好看,原因的话是因为这样修改相当于使用的是location.hash = 'xxx'
所以我们要用history的方式来变成网址
解决方法
- 在创建的router实例里面加上
mode: 'history'
就行了
重新进入刚才的网页,我们会发现网址变成了http://localhost:8080/home
,你懂的,要多爽有多爽
router-link补充
-
在前面的中,我们只是使用了
to
这个属性,现在我们来补充一些 -
tag
: tag可以指定<router-link>
在被渲染之后会变成什么标签,比如说如果我们加上tag='button'
,那么这个标签就会从默认的a
变成button
-
replace
: replace不会留下history记录,意思就是不能前进和后退, 这个属性没有属性值,写标签就可以了 , 还有就是我们的<router-link>
标签默认使用的是history.pushState()
-
active-class
- 我们会发现,哪个路由标签被选中,他的class类名中就会多出
router-link-active
这个类名,我觉得这个名字太难听了,想要改,用这个设置就可以了 - 当然,还有一种修改方式就是在router实例里面加上
linkActiveClass: '修改的类名'
就行了
- 我们会发现,哪个路由标签被选中,他的class类名中就会多出
当我们不想用router-link
标签来进行地址跳转的时候该怎么做
-
给标签绑定方法,在方法里面使用
this.$router.push('/xxx');
就可以了,和这个标签的效果是一样的,当然,也有this.$router.replace('/xxx')
-
注意: 不能使用history.pushState(),因为这样是绕过了vue-router
3.6 动态路由的使用
有些时候,一个页面的path路径是不确定的,比如说用户进入用户页面的时候,我们往往希望是下面的页面
user/用户名
,也就是说,后面这一段不是静态的,而是动态的- 所以我们要使用动态路由
步骤
-
在routes数组里面的path变成如下形式
-
在
app.vue
组件中,<router-link>
表现要这样写-
<router-link :to="'/user/' + name" tag="button">用户</router-link>
-
注意这里的name就是我们想要动态获取的
-
-
在路由组件的模板
-
<template> <div> <h2>我是用户页面</h2> <p>哈哈哈我是用户相关信息</p> <p>{{name}}</p> </div> </template> <script> export default { name: "User", data() { return { //这个代码就是核心,意思就是获取当前的活跃的路由的动态的uri那部分的参数 name: this.$route.params.id, } } } </script>
-
效果展示
我把name设置为张三
可以看到,当我们触发用户的时候,网址和页面的内容都是我们想要的,实现了动态路由
注意: this.$router
的意思是vue-router的实例, 而this.$route
的意思是获取当前活跃的路由对象
3.7 认识和使用路由的懒加载和路由的嵌套使用
- 官方给出的解释:
- 当打包构建应用的时候,JS包会变得非常大,影响页面的加载
- 如果我们能够把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了
路由的懒加载
怎么使用?
-
在注册组件的时候将其中的
component
以下列的形式注册-
component: () => import('引入的组件的路径')
-
只要这样做,那么在打包的时候就会将不同组件的js分批打包,当我们要用到它的时候,再从服务器请求过来,而不是一次性请求过来
我们会发现,编译出来的js文件不止3个,而是细分了更多个
路由的嵌套使用
其实就是路由里面再加一层路由,那么怎么做呢?
步骤如下
-
在component中创建新组件
-
在
index.js
里面,谁想要作为父组件,就在routes
数组里面的那个路由里面加上children
属性,具体写法如代码所示,注意子组件的路径不用加斜杠-
{ path: '/home', component: Home, children: [{ path: 'home1', component: Home1 }, { path: 'home2', component: Home2 } ] },
-
-
在父组件的模板里面加上
<router-link>
和<router-view>
,注意路径要向下面这样写-
<template> <div> <h2>我是首页</h2> <p>hhh</p> <router-link to="/home/home1"> home1</router-link> <router-link to="/home/home2"> home2</router-link> <router-view></router-view> </div> </template>
-
3.8 vue-router的参数传递
通过params传递
之前动态路由举的例子就是一个params的用法
使用要点
- 在
router/index.js
里面路由的配置,path: user/:userId
- 在
<router-link>
的to
属性这样配置::to="'/user/' + xxx"
,xxx就是userid的具体值 - 在子组件中,通过
this.$route.params.xxx
来得到,xxx就是我上面的userId - 反映在url上面,表现出来的样子就是
http://localhost:8080/user/zhangsan)
通过query传递(推荐)
但是,上面这个例子让我觉得这太垃圾了,我想用类似get请求得到的网页形式来作为url,即user?userid = xxx
, 那么这里我们就要你懂得
步骤如下
-
在
router/index.js
里面路由的配置,其实就是啥都不动-
{ path: '/user', component: User, }
-
-
在
<router-link>
的to
属性这样配置:to="{path: '/user' , query: {userId: 'zhangsan'} }
,注意,query里面可以放多个键值对,以逗号相隔就行 -
在组件中使用
this.$route.query.xxx就行了
,xxx就是你要获取的参数的key
-
反映在url上面,就是
http://localhost:8080/user?userId=zhangsan
,这个样子
3.9 导航守卫
我们想要监听路由的跳转的过程,那么导航守卫就发挥作用了
现在我们有一个需求,那么就是,每次我们跳转路由页面,那么html的title就会一起发生变化,那么我们要怎么做?
方法1: 利用生命周期函数
根据之前所学,我们常用的生命周期函数有下面几个
- mouted(): 组件刚挂载完成的时候回调
- created(); 组件没有被挂载,刚被创建的时候调用
- updated(): 组件的data发生更新的时候调用
-
那么我们就在每个页面的
created()
里面,使用dom来修改标题-
document.title = "xxx";
-
但是这样太麻烦了,每个页面都要加,很蠢
方法2: 利用导航守卫
-
在每个路由里面配置下面的属性
-
meta: { title: 'xx' },
-
-
在
router/index.js
里面使用下面的代码-
router.beforeEach((to,from,next) => { document.title = to.matched[0].meta.title next() })
-
-
就成功了
注意: 这个函数里面的to和from都是route对象,next是一个事先定义好的函数,这几个参数的含义顾名思义
导航守卫的补充
上面那个例子的函数叫做前置守卫(钩子)
- 相对的,也有一个函数叫做后置守卫 , 他是
afterEach()
, 他只有两个参数,一个是to,一个是from , 因此他不用向上面的这个函数一样调用next(),他会自动调用 - 两者的区别: 一个是在跳转前调用,一个是在之后
其他守卫
- 我们刚才的直接在
index.js
里面定义的守卫叫做全局守卫 - 除此之外还有: 路由独享守卫
beforeEnter()
afterEnter()
- 这两者都是在,
routes
数组里面的具体某个路由定义的
- 组件内守卫
beforeRouteEnter()
beforeRouteUpdate()
beforeRouteLeave()
- 三者都是在script组件内配置,层级和
data
,component
等一致
3.10 vue-router-keep-alive
介绍
每次我们跳转页面,我们会发现:调转之前的页面都被销毁了,所以 这个keep-alive就应运而生了
keep-alive: 他是vue-router
的一个组件,如果被包在keep-alive
里面,那么所有路径匹配到的视图组件都会被缓存
用法就是将<vue-view>
包裹在<keep-alive>
里面
被这个标签包裹之后,有两个类似生命周期的函数可以用
activated()
: 路由被激活的时候可以用deactivated()
: 路由不是当前页面的时候调用
可以用下列的代码来实现,记录子组件的路径,前提是,这个组件被套上了keep-alive
<script>
export default {
name: "Home",
data() {
return{
path: 'home/home1'
}
},
activated() {
this.$router.push(this.path)
},
beforeRouteLeave(to,from,next) {
this.path = from.path;
next();
}
}
</script>
属性介绍
- include - 参数是字符串或者正则表达式,只有匹配的组件才会被缓存
- exclude - 参数是字符串或者正则表达式,匹配的组件不会被缓存
有些时候,有些组件确实不想要被缓存,就是想要实时销毁和更新,比如档案文件啥的
实例
<keep-alive exclude="aaa,user">
<router-view>
</router-view>
</keep-alive>
//意思就是aaa和user这两个组件不会被缓存
r()2.
beforeRouteUpdate()3.
beforeRouteLeave()4. 三者都是在script组件内配置,层级和
data,
component`等一致
3.10 vue-router-keep-alive
介绍
每次我们跳转页面,我们会发现:调转之前的页面都被销毁了,所以 这个keep-alive就应运而生了
keep-alive: 他是vue-router
的一个组件,如果被包在keep-alive
里面,那么所有路径匹配到的视图组件都会被缓存
用法就是将<vue-view>
包裹在<keep-alive>
里面
被这个标签包裹之后,有两个类似生命周期的函数可以用
activated()
: 路由被激活的时候可以用deactivated()
: 路由不是当前页面的时候调用
可以用下列的代码来实现,记录子组件的路径,前提是,这个组件被套上了keep-alive
<script>
export default {
name: "Home",
data() {
return{
path: 'home/home1'
}
},
activated() {
this.$router.push(this.path)
},
beforeRouteLeave(to,from,next) {
this.path = from.path;
next();
}
}
</script>
属性介绍
- include - 参数是字符串或者正则表达式,只有匹配的组件才会被缓存
- exclude - 参数是字符串或者正则表达式,匹配的组件不会被缓存
有些时候,有些组件确实不想要被缓存,就是想要实时销毁和更新,比如档案文件啥的
实例
<keep-alive exclude="aaa,user">
<router-view>
</router-view>
</keep-alive>
//意思就是aaa和user这两个组件不会被缓存
注意: aaa和user是组件的
name
属性提供的