正课:
- 脚手架:
- 脚手架文件夹结构
- ES6的模块化开发
- 学子商城项目的制作过程:
- 懒加载:
- http-proxy:
一. 脚手架:
- 问题: 现代的前端项目结构非常复杂!如果任由什么人随意定义项目的文件夹结构,后果,项目与项目之前结构差异极大!即不便于维护,也不便于开发人员快速适应!
- 解决: VUE官方出了一套标准化的vue项目结构!所有使用vue框架开发的项目都要遵守这个标准化项目结构!——脚手架
- 好处: 几乎所有用vue做到项目,文件夹结构几乎是完全一样的!开发人员可以非常快速的适应新项目!
- 何时: 今后无论学习任何框架,先找脚手架。
- 如何: 2步:
(1).下载并安装一个可以反复生成脚手架代码的命令行工具: (老母鸡)
a. 说明: 只要在电脑上安装一次,就可反复为多个不同的项目分别创建脚手架项目结构
b. 如何: 2步:
1). 先配置npm的国内淘宝镜像,从国内服务器下载和安装:快
2种选一种:
a.npm config set registry http://registry.npm.taobao.org
npm 配置 设置 仓库
执行完之后,确认是否设置成功:npm config get registry
npm 配置 获得 仓库
看到返回淘宝镜像,说明成功!
b. 备选方案: 如果上一步出错!可选择这一步:
npm i -g cnpm --registry=http://registry.npm.taobao.org
;
确认是否安装成功:cnpm -v
看到版本号说明成功!
2). 下载并安装命令行工具: vue/cli, 2种选一种
a. 用npm安装:npm i -g @vue/cli
npm 安装 全局 command line interface
install global 命令 行 接口
//如果说FEXIST错误,可进入出错提示中的路径,默认为:
C:\Users\登录操作系统的用户名\AppData\Roaming\npm\node_modules
删除@vue文件夹
b. 备选方案: 如果npm出错,可换成: cnpm i -g @vue/cli
看到: + @vue/cli@4.5.8,说明安装成功
(2). 用脚手架为这个项目创建一套标准化的vue项目结构:
a. 先找到要创建项目的文件夹:在操作系统中找到想要创建项目的文件夹目录,然后在地址栏中输入cmd,按回车
b. 输入vue create 自定义项目名
1). Vue CLI v4.5.8
? Please pick a preset: (Use arrow keys)
Default ([Vue 2] babel, eslint)
Default (Vue 3 Preview) ([Vue 3] babel, eslint)
> Manually select features
2). ? Check the features needed for your project: (Press<space>
to select,<a>
to toggle all,<i>
to invert selection)
() Choose Vue version
> () Babel //翻译: 将时髦的ES6, ES7的语法,翻译为大多数浏览器都认识的ES5语法
( ) TypeScript
( ) Progressive Web App (PWA) Support
() Router
( ) Vuex
( ) CSS Pre-processors
() Linter / Formatter
( ) Unit Testing
( ) E2E Testing
3). ? Use history mode for router? (Requires proper server se
tup for index fallback in production) (Y/n) n
Vue的路由有两种模式:
hash模式:默认: #/相对路径 —— 不需要服务器端支持就可使用。
history模式:/相对路径 —— 需要服务器端配置首页重定向机制!
4). ? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
In dedicated config files
> In package.json
5). ? Save this as a preset for future projects? (y/N) N
c. 成功: 🎉 Successfully created project xzvue.
(3). 运行脚手架项目:
a. 删除项目中的.git文件夹
b. vscode打开刚生成的脚手架项目文件夹
c. 在vscode中,右键点击package.json文件,选择在集成终端中打开
d. 在弹出的终端窗口中: 输入npm run serve
遇到问题: 去看小程序->首页->VUE->day04 第一个问题: npm或vue命令行…
遇到: 无法将“npm”项识别为 cmdlet…
可以右键点击我的电脑->属性->高级系统设置->环境变量->下方系统变量列表中->双击path->新建->C:\Users\ 用户名\AppData\Roaming\npm
其中用户名换成你当前操作系统登录时的用户名。
e. 成功: DONE Compiled successfully in 2949ms
f. npm run serve,启动了一个临时的服务器,保存vue编译后的所有网页。地址: App running at:
- Local: http://localhost:8080/
所以,在vscode中,按住ctrl点这个链接地址,打开浏览器
g. ftp上:
xzvue 30M 完整的xzvue脚手架代码
xzvue 88K 去掉node_modules文件的简化版
解压,进入xzvue文件夹,在地址栏输入cmd, 再命令行输入npm i
就可以把去掉的node_modules重新下载下来!
二. 脚手架文件夹结构:
- 唯一完整的HTML页面(被拆成三分,运行时再组合起来): public/index.html
(1). 问题: index.html中即没有<router-view>
又没有new Vue()
(2). 原因:
a. vue的作者认为, 在index.html中不应该包含任何js代码!
b. 作者认为,vue中一切都是组件,连<div id="app">
也是一个组件。所以,一定要将<div id="app">
也放在一个独立的组件文件里。
(3). 解决:
a. 原来index.html中的new Vue()等js代码,都集中保存在src/main.js文件中
b. 原来index.html中的<div id="app">
,都集中保存在src/App.vue文件中
c. 在用npm run serve编译整个项目时:
1). 用App.vue中的<div id="app">
代替index.html中临时占位的空的<div id="app">
2). 将main.js中的new Vue()等js代码都重新引入回index.html页面中。 - 页面组件(项目中共有几个页面,就要包含几个页面组件)
(1). src/views文件夹下
(2). 问题:
a. 在js文件中写组件的template时,没提示,很痛苦!
b. css写哪儿?
(3). 解决: Vue中独创了一种新的文件格式: .vue文件
a. Vue中每个组件,都要创建为一个.vue文件
b. 每个.vue文件包含三部分内容:
1). 专门编写HTML代码片段的区域:<template>
如果希望有提示,需要安装vscode的插件vetur
2). 专门编写组件对象的区域:<script>
export default {
//页面组件的内容
}
3). 专门编写CSS样式的区域: `<style>`
- 路由器对象:
src/router/index.js
,也包含路由字典routes和路由器对象router。 - 全局组件或子组件:
src/components
三. ES6的模块化开发:
- 问题: 明明是router.js中需要用到index组件和details组件,但是我们却必须将index组件和details组件都引到index.html网页中!——乱
- 解决: 脚手架中采用了ES6的模块化开发!可以让模块与模块之间直接引用!——直观,简洁
- 如何: 2步
(1). 将所有组件都变成模块对象:
a. 默认: 一个不带js的.vue文件,默认就是一个模块对象。
b. 如果一个.vue文件中带有js组件对象,则必须用export default {}抛出后,才能让别人引入!
(2). 如果一个组件/模块内,需要用到另一个模块的内容,就可用import引入其它模块,为自己所用
import 自定义变量名 from “相对路径”
从指定路径的文件中
引入一个模块对象
保存在自定义的变量名中
四. 避免组件间样式冲突
-
问题: 在不同的页面组件中使用相同的选择器规定了不同的css样式,结果,只有其中一个套css样式可以保留!发生了冲突!
-
原理: 所有组件的css代码,最终会被打包压缩为一个css文件。将来所有组件的css,其实是放在一个文件中的。如果一个文件中出现完全相同的两个选择器,则只要后一个才能生效!
-
解决: 2种:
(1). 自动化(不是万能, 效率低):
a.<style scoped>
(当前组件)范围内的
b. 意为: 将以下css样式限制在仅当前组件内的元素可用!其它组件的元素不可用
c. 原理:
1). scoped会自动给当前组件的每个元素都添加一个相同的随机的自定义属性名。<元素 data-v-随机名>
2). 自动将当前组件内的所有选择器结尾加一个附加条件:
其它选择器[data-v-随机名]
结果: 只有带有data-v-随机名的元素才能使用这个选择器
3). scoped可以保证每个组件的随机名是不一样的!就实现了一个组件,不能使用其它组件中的样式!(2). 手动解决(万能):
a. 如何: 2步:
1). 每创建一个组件,都在组件的唯一父元素上,定义一个专门的class名(尽量和组件名相同)
2). 在<style>
中凡是这个组件中的选择器,必须以".这个组件名"开头
b. 示例:
Home.vue
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<h1>欢迎 {{uname}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
uname:"东东"
}
}
}
</script>
<style>
.home>h1{
background-color:red;
color:yellow;
}
</style>
About.vue
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
.about>h1{
background-color:green;
color:#fff;
}
</style>
五. 懒加载:
- 问题: 单页面应用最大问题: 首页慢!
- 原因: 因为首次请求,就要把所有组件内容都下载下来!
- 解决: 懒加载,需要什么,才加载什么;不需要什么,就不加载!2种:
(1). 异步延迟加载:
a. 什么是:优先下载首页,其余页面组件,采用在底层异步延迟下载的方式,悄悄下载到客户端。
b. 优点: 折中: 即不影响首页加载速度,又能保证将来切换页面时,只在客户端很快切换
c. 缺点: 万一用户不想看其余页面,也下载下来,有点儿浪费流量!
(2). 真正的懒加载:
六. http-proxy代理跨域:
-
问题:
(1). CORS: 优点: 仅服务器端修改即可; 缺点: 必须知道客户端的具体IP地址
(2). JSONP: 优点: 不需要知道客户端具体IP地址; 缺点: 需要客户端和服务器端都作出调整!
(3). 如果服务器端觉得CORS也不好用,JSONP也不好用,干脆就没有做跨域!单靠前端能否跨域? -
解决: 利用vue脚手架中自带的http-proxy代理程序来跨域访问。详细信息可查看官方文档: https://cli.vuejs.org/config/#devserver-proxy
今后,只要别人问你们公司的项目用的什么跨域,都说:http-procy(http代理) -
什么是HTTP代理(http-proxy): 在vue客户端项目中,用js临时创建一个小程序,发送http请求(不是ajax)。由这个底层的小程序负责发送请求,接收响应。而客户端vue项目只需要将自己的请求地址交给底层小程序,然后,等着从小程序获取响应结果即可!
-
如何:
(1). 在vue脚手架配置文件(vue.config.js)中,添加一个新的配置项
module.export={
... ... ,
devServer: {
proxy: {
'/api': { //为所有服务器端接口起一个别名前缀,为了和vue脚手架中其它页面的路由地址区分
target: `http://服务器端接口地址统一前缀`,
changeOrigin: true, //跨域
pathRewrite: {
//因为真实的服务器端地址中是不包含/api的,所以
'^/api' : '' //应该将程序中的/api删除(替换为空字符串),再和target中的基础路径拼接起来作为发送到服务器的最终请求地址。
}
}
}
}
}
(2). 在main.js中,axios的baseURL就不能写真实的服务器地址,应该写devServer中定义的所有服务器端地址的别名前缀:
axios.defaults.baseURL="/api"
(3). 在组件内使用axios发送请求时,依然使用接口的相对地址就可以: this.axios.get("/index")。
- 原理:
this.axios.get("/index")
↓axios会将baseURL和"/index"拼接
this.axios.get("/api/index")
所有↓带有/api前缀的路径都会交给devServer程序处理
devServer —— 不使用ajax,也能发送http请求的程序
↓先将相对路径中的/api去掉
/index
↓再和提前配置好的这是服务器地址target拼接
http://localhost:5050/index
↓向真实的服务器接口地址发送请求
devServer接到响应结果
↓
返回给axios.then(result=>{})继续处理
整个过程因为没有用到ajax,所以不受浏览器同源策略的限制。
- 强调: 只有访问没有启用跨域的服务器端时,才能使用http-proxy。不能和其它跨域方式并存的。
总结
- SPA
(1). 3步:
a. 先创建唯一完整的HTML页面
1). 包含vue基本的页面结构
<div id="app"> new Vue({el:"#app"})
2). 引入所有必要的文件和组件
vue-router.js, 其它页面或组件文件, router.js路由器对象所在文件
3). <div id="app">
中用<router-view/>
为今后页面组件预留空位
b. 再为每个页面组件创建独立的文件。每个页面组件其实都是一个子组件
c. 创建router.js文件,创建路由器对象
1). 创建路由字典对象:
var routes=[
{path:"/", component:首页组件对象名},
{path:"/相对路径" , component: 其它页面组件对象名},
{path:"*", component: 404页面组件对象 }
]
2). 创建路由器对象,并将路由字典对象转入路由器对象中
var router=new VueRouter({ routes })
3). 将router对象加入到new Vue()中
回到唯一完整的HTML页面中: new Vue({ el:"#app", router })
(2). 页头等全局组件:
a. 创建独立的文件保存页头组件的内容
b. 使用Vue.component("my-header",{ ... })
将页头创建为全局组件
c. 在唯一完整的HTML页面中引入页头组件文件
d. 使用页头组件标签<my-header/>
: 2种:
1). 如果所有页面都有统一的页头:
就放在唯一完整的html页面中<router-view>
外部上方
2). 如果有的页面有页头,有的页面没有页头:
就只放在需要页头的组件中的template中
(3). 路由跳转: 2种:
a. html中: <router-link to="/相对路径">文本<router-link>
b. js中: this.$router.push("/相对路径")
(4). 路由传参:
a. 修改路由字典:
{path:"/相对路径/:自定义参数名", component:页面组件对象, props:true}
b. 跳转时:
<router-link to="/相对路径/参数值"
或
this.$router.push("/相对路径/参数值")
c. 下一个页面接:
1). props:[ ‘自定义参数名’ ]
2). 可将"自定义参数名"用作绑定或程序中都行
11. 脚手架文件夹结构:
(1). 唯一完整的HTML页面: 一分为三:
a. public文件夹
1). 图片img文件夹放在public文件夹下
2). 第三方css的压缩版和第三方js的压缩版都放在public文件夹下
3). 唯一完整的HTML文件index.html中,head中引入第三方的css和js
b. src/App.vue
1). <template>
下只包含公共的页头组件和<router-view>
2). <style>
下包含所有网页都要用到的公共css样式,比如css重置代码
c. src/main.js
1). import引入App.vue,router,axios,以及其他全局组件
2). 将全局组件对象转为真正的全局组件: Vue.component( “组件标签名”, 全局组件对象 )
3). 配置axios并放入原型对象中:
axios.defaults.baseURL="服务器端基础路径"
Vue.prototype.axios=axios;
(2). 为每个页面创建.vue组件文件,都放在src/views文件夹下。每个.vue文件中:
a. <template>
标签内,包含这个页面的HTML内容
b. <script>export default{ ... }</script>
中包含组件对象的js内容。
c. <style>
标签内包含仅这个页面组件内才使用的css
d. <template>
中的HTML内容以及<script>export default{...}</script>
中的js内容,和前四天将的是完全一样的写法,绑定,指令,函数,生命周期,axios请求等都一样。前四天怎么用,这里就怎么用。
(3). 路由字典和路由器对象,在src/router/index.js
文件中
a. 仅import首页组件对象,不要过早引入其它页面组件
b. 路由字典中首页组件: { path:"/", component:首页组件对象}
c. 其余页面组件都做成懒加载:
{
path: '/相对路径',
component: () => import(/* webpackChunkName: "组件名" */ '../views/其它页面组件.vue')
}
(4). 全局组件都放在src/components
文件夹下,每个全局组件.vue文件中。但是,全局组件必须在main.js引入,并用Vue.component()
转化为真正的全局组件,才能在其它组件的HTML中使用。
(5). 运行时: 路由器router对象监视浏览器地址栏路径变化,并查找对应的页面组件内容,先代替App.vue中的<router-view>
,然后main.js再将包含要加载的页面内容的App.vue组件内容,替换到唯一完整的index.html中空<div id="app">
位置。
12. 懒加载:
(1).异步延迟下载:
src/router/index.js
//不要import Details from "../views/Details"
const routes=[
...
{
path:"/details/:lid",
component: () => import(
/* webpackChunkName: "details" */
'../views/Details.vue'
),
props:true
]
(2).彻底懒加载:
项目根目录创建vue.config.js
module.exports={
chainWebpack:config=>{
config.plugins.delete("prefetch")
//删除index.html开头的带有prefetch属性的link,不要异步下载暂时不需要的页面组件文件
},
}
- http-proxy方式跨域:
在服务器端没有配置CORS或JSONP跨域的情况下:
(1). vue.config.js中
module.exports={
... ,
devServer: {
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
'/api': {
target: `http://服务器端接口地址统一前缀部分`,
changeOrigin: true,
pathRewrite: {
'^/api' : '' //将程序中的/api,替换为空字符串,再和target中的基础路径拼接起来作为发送到服务器的最终请求地址。
}
}
}
}
}
(2). main.js中:
…
axios.defaults.baseURL="/api"
14. 避免组件间样式冲突:
(1)<style scoped>
(2)<template>
<组件父元素 class="组件名">
</template>
<style>
.组件名>子元素选择器{ ... }
</style>
- watch+事件修饰符+防抖:
(1). 希望变量一变化就自动执行一项操作时:
data(){
return { 变量: 值 }
},
watch:{
变量(){
要执行的操作
}
}
(2). 事件修饰符:
@事件.13 按回车时才执行
@事件.stop 阻止冒泡
@事件.prevent 阻止默认行为
(3). 防抖:
data(){
return {
... ,
timer:null
}
},
methods:{
查找方法(){
if(this.timer!=null){
clearTimeout(this.timer)
}
this.timer=setTimeout(()=>{
正式的查找操作
},等待时间)
}
}