目录
1. 唯一完整的HTML页面被拆分为三部分: public/index.html
4. 全局组件或子组件: src/components文件夹下
【前文回顾】👉 vue中如何实现SPA 单页面应用_09
一. vue脚手架
1. 前端项目结构存在的问题
现代的前端项目结构非常复杂!如果任由什么人随意定义项目的文件夹结构,后果,项目与项目结构差异极大!即不便于维护,也不便于开发人员快速适应!
2. 如何解决
VUE官方出了一套标准化的vue项目结构!所有使用vue框架开发的项目都要遵守这个标准化项目结构!——脚手架
3. 使用脚手架的好处
几乎所有用vue做的项目,文件夹结构几乎是完全一样的!开发人员可以非常快速的适应新项目!
4. 何时使用脚手架
今后无论学习任何框架,先找脚手架。
5. 如何使用脚手架
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 // 选择vue脚手架的版本
> (*) Babel // 转码器: 将时髦的ES6, ES7的语法,翻译为大多数浏览器都认识的ES5语法
() TypeScript
() Progressive Web App (PWA) Support // 渐进式Web应用程序
(*) Router // VueRouter,SPA应用的核心组件,即vue-router(vue路由组件)
() Vuex // vuex(vue的状态管理模式)
(*) CSS Pre-processors // CSS 预处理器(如:less、sass)—支持Scss语法,可选可不选
> (*)Sass/SCSS (with dart-sass)
() Sass/SCSS (with node-sass)LessStylus
🎈Sass/SCSS(with node-sass) 和 Sass/SCSS(with dart-sass)选哪个?
👉 node-sass是自动编译实时的,dart-sass需要保存后才会生效。
👉 sass 官方目前主力推dart-sass 最新的特性都会在这个上面先实现。
() Linter / Formatter // 代码风格检查和格式化工具(如:ESlint)要求比较严格,不建议选
() Unit Testing // 单元测试(unit tests)
() E2E Testing // e2e(end to end) 测试
3). ? Use history mode for router? (Requires proper server setup 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文件夹(可保留,若你没使用vscode进行git版本(代码)管理,可删除)
b. vscode打开刚生成的脚手架项目文件夹
c. 在vscode中,右键点击package.json文件,选择在集成终端中打开
d. 在弹出的终端窗口中: 输入 npm run serve
如果遇到报错: 无法将“npm”项识别为 cmdlet...
可以右键点击我的电脑->属性->高级系统设置->环境变量->下方系统变量列表中->双击path->新建->C:\Users\用户名\AppData\Roaming\npm(其中用户名换成你当前操作系统登录时的用户名)
e. 成功: DONE Compiled successfully in 8922ms
f. npm run serve,启动了一个临时的服务器,保存vue编译后的所有网页。地址: App running at:
- Local: http://localhost:8080/
所以,在vscode中,按住ctrl点这个链接地址,打开浏览器
二. 脚手架文件夹结构
1. 唯一完整的HTML页面被拆分为三部分: public/index.html
唯一完整的HTML页面(被拆成三分,运行时再组合起来)
(1). 问题: index.html中即没有<router-view>又没有new Vue()
(2). 原因:
a. vue的作者认为, 在index.html中不应该包含任何js代码!
b. 作者认为,vue中一切都是组件,连<div id="app">也是一个组件。所以,一定要将<div id="app">也放在一个独立的组件文件里。
(3). 解决:
唯一完整的HTML页面被拆分的三部分为:index.html、App.vue、main.js
a. 原来index.html中的new Vue()等js代码,都集中保存在src/main.js文件中
b. 原来index.html中的<div id="app">,都集中保存在src/App.vue文件中
<div id="app"> <router-view></router-view> </div>
c. 在用npm run serve编译整个项目时:
1). 用App.vue中的<div id="app">代替index.html中临时占位的空的<div id="app">
2). 将main.js中的new Vue()等js代码都重新引入回index.html页面中。
2. 页面组件: src/views文件夹下
页面组件(项目中共有几个页面,就要包含几个页面组件)
(1). src/views文件夹下
(2). 问题:
a. 在js文件中写组件的template时,没提示,很痛苦!
b. css写哪儿?
(3). 解决: Vue中独创了一种新的文件格式: .vue文件
a. Vue中每个组件,都要创建为一个.vue文件
b. 每个.vue文件包含三部分内容:
1). 专门编写HTML代码片段的区域: <template>
如果希望有提示,需要安装vscode的插件vetur
2). 专门编写组件对象的区域: <script></script>
export default {
//页面组件的内容
}
3). 专门编写CSS样式的区域: <style></style>
3. 路由器对象: src/router/index.js
路由器对象: src/router/index.js,也包含路由字典routes和路由器对象router。
4. 全局组件或子组件: src/components文件夹下
三. ES6的模块化开发
1. 问题
明明是router.js中需要用到index组件和details组件,但是我们却必须将index组件和details组件都引到index.html网页中!——乱
2. 解决
脚手架中采用了ES6的模块化开发!可以让模块与模块之间直接引用!——直观,简洁
😇 比如说router.js中需要用到index组件和details组件,就可以直接引入,而不需要再绕到index.html网页引用
3. 如何
如何实现模块化开发,2步:
(1). 将所有组件都变成模块对象: (只有模块才能被引入,所以先要成为模块)
a. 默认: 一个不带js的.vue文件,默认就是一个模块对象。
😇 有的.vue文件,是没有js的,而且有的.vue文件也没有css,比如有的.vue文件(组件)只有html(template)。一个模块对象不一定都有js或是css,只有html(template)的.vue文件就是一个vue默认的模块对象
b. 如果一个.vue文件中带有js组件对象,则必须用export default {}抛出模块对象后,才能让别人引入!
(2). 如果一个组件/模块内,需要用到另一个模块的内容,就可用import引入其它模块,为自己所用
import 自定义变量名 from "相对路径"
从指定路径的文件中
引入一个模块对象
保存在自定义的变量名中
😇 扩展:.vue模块文件的后缀名可省略
使用import引入其他模块时,如果模块文件是.vue结尾,则可以省略.vue后缀名,如:import Detail from ”../views/detail.vue”,其中,.vue可省略;
如果如果模块文件是index.js,则引入时可以省略index.js,如:import router from “../router/index.js, 其中,index.js可省略。只要是引入模块,文件夹下的文件是index.js,都可以省略(只要文件夹下js文件是index.js,就可以省略)。可以查看学习node.js文件夹模块
😇 关于何时使用 ../ 与 ./ —— 看有没有必要向上,如有必要使用 ../ 而平级的话则用 ./
四. 避免组件间样式冲突
1. 问题
在不同的页面组件中使用相同的选择器规定了不同的css样式,结果,只有其中一套css样式可以保留!发生了冲突!
2. 原理
所有组件的css代码,最终会被打包压缩为一个css文件。将来所有组件的css,其实是放在一个文件中的。如果一个文件中出现完全相同的两个选择器,则只有后一个才能生效!
3. 解决
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>
运行结果:
扩展:使用v-html绑定元素内容时,该元素内容的样式需要使用全局
<style>
元素进行设置在单文件组件里,
scoped
的样式不会应用在v-html
内部,因为那部分 HTML 没有被 Vue 的模板编译器处理。如果你希望针对v-html
的内容设置带作用域的 CSS,你可以替换为 CSS Modules 或用一个额外的全局<style>
元素手动设置类似 BEM 的作用域策略。
参考vue官网v-html:https://cn.vuejs.org/v2/api/#v-html
五. watch+事件修饰符+防抖debounce
1. 问题
如何实现一边输入,一边就执行操作!
2. 解决
使用vue中的监视函数watch
3. 何时
今后只要希望一个变量一发生变化,就能立刻执行一个我们自定义的任务!
4. 如何
组件对象中{
... ,
data(){
return {
变量:值
}
},
watch:{
变量(){
要执行的操作!
}
}
}
5. 结果
只要变量一发生变化,就自动调用watch中的同名函数
6. 问题
如何实现在文本框上点回车也能搜索?
7. 解决
<input @keyup="search">
8. 问题
@keyup的问题:
(1). 当@keyup和watch同时存在时,执行的操作可能重复执行两遍: 内容变化,变量变化,watch执行;又因为触发了keyup,所以事件处理函数也执行。
(2). 除了按字母和回车键之外,按键盘上其它无关的按键也会触发搜索操作!特别没必要!
(3). 应该只有按照回车键时才能执行搜索操作
9. 解决
就要用vue中的事件修饰符:
(1). 事件修饰符: 对传统的DOM事件对象中常用方法和属性的简写。
(2). 想判断键盘按键的编号:
a. DOM中: if(e.keyCode==13)
b. vue中: @keyup.13 //只有按在13号键上才执行操作
(3). 其它事件修饰符常用的操作:
a. 停止冒泡:
1). DOM中: e.stopPropagation()
2). vue中: @事件名.stop="处理函数"
b. 阻止默认行为:
1). DOM中: e.preventDefault()
2). vue中: @事件名.prevent="处理函数"
10. 问题
如果用户还没有输入完关键词,就去服务器频繁查找,既没必要,又浪费服务器端资源,降低效率!
11. 解决
防抖(debounce)
(1). 什么是: 必须等待用户操作停止后一段时间,才执行正式的操作!
(2). 何时: 今后只要希望阻止用户频繁操作向服务器发送请求时,都可以用防抖。
(3). 如何: 定时器
data(){
return {
... ,
timer:null
}
},
methods:{
查找方法(){
if(this.timer!=null){
clearTimeout(this.timer)
}
this.timer=setTimeout(()=>{
正式的查找操作
},等待时间)
}
}
六. 懒加载
1. 问题
单页面应用最大的问题: 首页加载极慢!
2. 原因
因为单页面应用默认在首次请求时,必须把所有页面组件都下载到客户端本地!
3 原理
npm run serve时,vue脚手架会将所有.vue文件的代码都打包压缩到一个js文件中,再引入唯一完整的index.html文件中。所以,这个包含所有代码的js文件会很大!
4. 解决
懒加载,需要什么,才加载什么;不需要什么,就不加载!2种:
(1). 异步延迟加载(脚手架默认):
a. 什么是: 优先下载首页,其余页面组件,采用在底层异步延迟下载的方式,悄悄下载到客户端。
👉 暂时不要把除首页之外的其它代码import进程序中!
👉 在路由字典中定义,只有当用户访问到某个组件时,才动态引入这个组件。
src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from "../views/Index"
Vue.use(VueRouter)
const routes = [
{
path: '/',
component: Index
},
{
path: '/details/:lid',
//暂时不要把Details.vue引入项目
//只有当用户访问到details时,才将Details组件引入项目中
//而且webpackChunkName:是在指示,将Details.vue文件单独打包压缩为一个文件。文件名为details
component: () => import(/* webpackChunkName: "details" */ '../views/Details.vue'),
props:true
}
]
const router = new VueRouter({
base: process.env.BASE_URL,
routes
})
export default router
b. 优点: 折中: 即不影响首页加载速度,又能保证将来切换页面时,只在客户端很快切换
c. 缺点: 万一用户不想看其余页面,也下载下来,有点儿浪费流量!
问题:👀 虽然不会在首屏下载details.js,但是却会在后台异步下载details.js,偷跑流量!
(2). 真正的懒加载:
彻底的懒加载⏬
a. 如果用户不访问其它页面,则根本一点儿都不下载其它页面的内容
b. 如何: 在vue脚手架中添加一个配置信息:
1). 在vue脚手架项目的根目录下创建vue.config.js
2). 在vue.config.js中添加以下固定配置信息,禁止提前下载其它组件
module.exports = {
chainWebpack: config => {
config.plugins.delete("prefetch")
//删除index.html开头的带有prefetch属性的link,不要异步下载暂时不需要的页面组件文件
},
}
c. 结果: 除首页之外的其它页面组件内容,一点都不提前下载。只有当用户点连接,访问到一个页面时,才临时下载这个页面组件的内容
d. 问题: 因为当用户访问页面时,临时去下载页面的内容,加载速度稍慢。
💭 关于懒加载的疑问
🎈 你的疑惑
有人会问,只有当用户点连接,访问到一个页面时,才临时下载这个页面组件的内容,这跟多页面应用(都是下载后,才能拿过来看)不就没有什么区别了吗?
🎈 个人理解首先,下载的是个片段,而非整个页面,这比多页面应用的请求的整个页面小多了,其次,彻底的懒加载只需要下载一次,保存到客户端的,今后可以反复看,以前的多页面应用是不行的,多页面应用是不缓存的。所以,临时下载虽然慢了一点,但比起多页面应用来说优势还是很明显的
七. 使用脚手架创建一个vue项目
我们先来介绍一下,今后vue脚手架项目中必用的请求工具axios,是如何安装、配置和并引入组件的
🚀 安装和配置axios:
(1). 脚手架默认不带axios
(2). 安装: npm i -save axios
安装在当前项目内部
(3). 配置:
a. 在main.js中, new Vue()之前就要配置
b. 引入axios模块
import axios from "axios" // node_modules中
c. 配置axios的基础路径:
axios.defaults.baseURL="http://服务器端接口的基础地址"
d. 问题: 几乎所有组件中都要用到axios
前提: 无论是new Vue(),还是Vue.component(), 还是export default {},其实本质都是Vue类型的一种子对象
解决: 所以axios应该放在Vue类型的原型对象中!
Vue.prototype.axios=axios
(4). 使用: 在任何一个组件的任何一个位置,都可用:
this.axios.get/post(...).then(result=>{ ... result.data ... })
🔨 脚手架项目完整的文件夹结构,如下图所示:
🔗 vue项目各组件源码如下:
◼️ public/index.html 👈唯一完整的html页面
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
◼️ src/App.vue 👈唯一的根组件(汇总所有组件)
<template>
<div id="app">
<my-header></my-header>
<router-view/>
</div>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
◼️ src/components/MyHeader.vue 👈页头组件——全局组件
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
</template>
<style>
#nav {
padding: 30px;
}
#nav a {
font-weight: bold;
color: #2c3e50;
}
#nav a.router-link-exact-active {
color: #42b983;
}
</style>
◼️ src/views/Home.vue 👈主页组件
<template>
<!-- 专门编写组件的HTML内容 -->
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<h1>我的第一个脚手架项目!</h1>
<h2>我的用户昵称:{{ uname }}</h2>
<h3>用户ID:{{ uid }}</h3>
<button @click="search">我的CSDN个人主页</button>
</div>
</template>
<script>
//专门包含组件对象
export default {
data() {
return {
uname: "儒雅的烤地瓜",
uid: "sunyctf",
};
},
methods: {
search() {
alert("欢迎访问CSDN!");
window.location.href = `https://blog.csdn.net/sunyctf`
},
},
};
</script>
<style>
/* 引入外部的CSS文件 */
@import url(../assets/css/home.css);
</style>
◼️ src/views/About.vue 👈关于页组件
<template>
<div class="about">
<h1>This is an about page</h1>
<button @click="search">我的CSDN个人主页</button>
</div>
</template>
<script>
export default{
methods: {
search() {
alert("欢迎访问CSDN!");
window.location.href = `https://blog.csdn.net/sunyctf`;
}
}
}
</script>
<style>
/* 引入外部的css文件 */
/* 方式一 */
@import url(../assets/css/about.css);
/* 也可以直接使用路径 */
/* @import '../assets/css/about.css'; */
/* 方式二 */
/* <style src='../assets/css/about.css'></style> */
</style>
◼️ src/main.js 👈项目入口js文件
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import axios from "axios"
import MyHeader from "./components/MyHeader.vue"
Vue.component("my-header",MyHeader);
axios.defaults.baseURL="http://localhost:8080";
// 将axios类库添加到vue的原型对象中,这样每个页面都可以使用
Vue.prototype.axios=axios;
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
◼️ src/router/index.js 👈路由器文件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
// 懒加载
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
routes
})
export default router
◼️ src/assets/css/home.css 👈主页样式文件
/* 解决避免组件间样式冲突的方法:
* 1). 每创建一个组件,都在组件的唯一父元素上,定义一个专门的class名(尽量和组件名相同)
* 2). 在<style>中凡是这个组件中的选择器,必须以".这个组件名"开头
*/
.home>h1 {
background-color: red;
color: yellow;
}
.home>button {
width: 300px;
height: 50px;
border-radius: 5px;
background-color: aqua;
border: 0px;
font-size: 21px;
color: white;
font-weight: bolder;
}
◼️ src/assets/css/about.css 👈关于页样式文件
/* 解决避免组件间样式冲突:
* 1). 每创建一个组件,都在组件的唯一父元素上,定义一个专门的class名(尽量和组件名相同)
* 2). 在<style>中凡是这个组件中的选择器,必须以".这个组件名"开头
*/
.about>h1 {
background-color: aqua;
color: coral;
}
.about>button {
width: 300px;
height: 50px;
border-radius: 5px;
background-color: rgb(0, 110, 255);
border: 0px;
font-size: 21px;
color: white;
font-weight: bolder;
}
🎈 运行脚手架项目:
页面效果预览:
🛎️ 注意:Vue的路由器采用锚点地址,作为客户端导航的相对地址
例如:http://127.0.0.1:8080/index.html#/相对路径 👈路由器对象router监控的就是#后的地址
💥 扩展:this判断—8种指向
this 8种指向: 判断this,一定不要看定义在哪儿!只看调用时!
➡️ 1. obj.fun() this->obj
➡️ 2. fun() 或 (function(){ ... })() 或 多数回调函数 或 定时器函数 this->window
➡️ 3. new Fun() this->new正在创建的新对象
➡️ 4. 类型名.prototype.共有方法=function(){ ... } this->将来谁调用指谁,同第一种情况
➡️ 5. DOM或jq中事件处理函数中的this->当前正在触发事件的DOM元素对象
如果需要使用简化版函数,必须$(this)
➡️ 6. 箭头函数中的this->箭头函数外部作用域中的this
➡️ 7. jQuery.fn.自定义函数=function(){ ... } this->将来调用这个自定义函数的.前的jQuery子对象,不用再$(this)
➡️ 8. new Vue()中methods中的函数中的this->当前new Vue()对象
❣️ 总结:知识点提炼
1. MVVM: 界面View+模型Model+视图模型ViewModel
2. Vue绑定原理: 访问器属性+虚拟DOM树
变量被修改时: 访问器属性发出通知,虚拟DOM树扫描并仅更新受影响的元素
3. 虚拟DOM树优点:
(1). 小: 只包含可能变化的元素。
(2). 遍历查找快
(3). 修改效率高: 只修改受影响的元素。
(4). 避免重复编码: 已封装DOM增删改查代码
4. Vue功能3步:
(1). 先创建增强版的界面:
a. 整个界面必须包含在一个唯一的父元素下:
通常是<div id="app">
b. 可能变化的元素内容用{{自定义变量名}}标记
c. 触发事件的元素用@click="自定义处理函数名"标记
(2). 再创建new Vue()对象,其中el:指向new Vue()要监控的页面区域
(3). 在new Vue()对象内定义模型对象data和methods
a.界面所需的所有变量都放在data中
b.界面所需的所有事件处理函数都放在methods中
5. 总结: 绑定语法+13种指令
(1). 如果元素的内容需要随变量自动变化: {{}}
(2). 如果元素的属性值需要随变量自动变化: :
(3). 控制一个元素显示隐藏: v-show //使用display:none隐藏元素
(4). 控制两个元素二选一显示: v-if v-else //使用删除元素方式隐藏元素
(5). 多个元素多选一显示: v-if v-else-if v-else
(6). 只要反复生成多个相同结构的元素组成列表时: v-for :key="唯一标识"
强调: 为什么必须加:key="i"?给每个元素副本添加唯一标识。修改数组中某个元素值时,避免重建整个列表,只需要修改一个DOM元素副本即可!提高修改效率。
(7). 只要绑定事件: @ $event
(8). 防止用户短暂看到{{}}: v-cloak和v-text
(9). 只要绑定原始HTML代码片段内容: v-html
(10). 如果元素的内容只在首次加载时绑定一次,之后都不会改变: v-once
优化: 减少虚拟DOM树中元素个数。
(11). 保护内容中的{{}}不被编译: v-pre
(12). 今后只要想获得表单元素的值或状态: v-model
6. 绑定样式:
(1). 需要精确修改某一个css属性,就绑定style:
a. <元素 style="固定样式" :style="{css属性:变量名, ...}"
data:{
变量名:css属性值
... : ...
}
b. <元素 style="固定样式" :style="变量名"
data:{
变量名:{
css属性名: 属性值,
... : ...
}
}
(2). 只要批量修改一个元素的多个css属性就绑定class
a. <元素 class="固定class" :class="{class名:变量名, ...}"
data:{
变量名:true或false,
... : ...
}
b. <元素 class="固定class" :class="变量名"
data:{
变量名:{
class名:true或false,
... : ...
}
}
7. 只要希望在页面加载时自动对元素执行一些初始化操作时就用自定义指令:
(1). 添加自定义指令:
Vue.directive("自定义指令名",{
inserted(domElem){
对domElem执行DOM操作
}
})
(2). 使用自定义指令:
<元素 v-自定义指令名>
8. 今后只要根据其他变量的值动态计算出一个属性值就用计算属性:
<元素>{{计算属性}}</元素>
new Vue({
el:"#app",
data:{...},
methods:{...},
computed:{
计算属性名(){
计算过程
return 计算结果
}
}
})
9. 希望将变量的原始值先加工后再显示给用户看时就用过滤器:
Vue.filter("过滤器名",function(oldVal, 自定义形参,...){
return 加工后的新值
})
<元素>{{ 变量 | 过滤器(实参值, ...) | ... }}</元素>
10. 只要在vue中发送ajax请求,就用axios
axios.defaults.baseURL="服务器端接口的公共基础地址部分"
axios.get(
"服务器端接口地址的相对路径",
{
params:{ 参数名: 参数值, ... }
}
).then(result=>{
... result.data...
})
或
axios.post(
"服务器端接口地址的相对路径",
"参数名1=参数值1&参数名2=参数值2&..."
).then(result=>{
... result.data...
})
强调: 在vue内使用axios,then中必须用箭头函数,保持then内this与外部this一致,都指向当前new Vue()对象
11. vue生命周期4个阶段 8个钩子函数
beforeCreate(){ ... }
(1). 创建(create)
created(){ ... }
beforeMount(){ ... }
(2). 挂载(mount)
mounted(){ ... 经常在这里发送ajax请求 ... }
beforeUpdate(){ ... }
(3). 更新(update)
updated(){ ... }
beforeDestroy(){ ... }
(4). 销毁(destroy)
destroyed(){ ... }
12. 只要希望重用一块独立的功能区域就用组件:
(1). 定义组件
Vue.component(`组件标签名`,{
template:`HTML内容片段`,
data(){ return { 变量 } },
//其余和new Vue()完全相同
})
(2). 在HTML中使用自定义组件
<组件标签名/>或双标记也行
(3). 原理: new Vue()扫描到自定义组件标签,
a.组件的template中的HTML内容代替页面中<组件标签>位置。
b. 并为这个小区域专门创建一个缩微版的vue类型对象。
1). 调用组件的data()函数为当前组件副本创建一个专属数据对象副本。
2). 引入组件对象中的methods等其他内容到当前组件对象副本中
13. 组件化开发:
(1). 步骤:
a. 拿到页面先划分功能区域
1). 从上到下,按功能不同划分区域
2). 按是否重用划分
b. 为每个组件创建独立的.js文件,其中包含一个组件对象及其内容
c. 将所有组件引入唯一完整的html页面中,并在<div id=”app”></div>中添加父组件标签。
(2). 运行时:
a. new Vue()扫描<div id=”app”>,发现父组件标签,创建并替换父组件
b. 父组件扫描自己内部的template内容,创建并替换子组件
(3). 三种组件:
a. 根组件: new Vue()
b. 全局组件: Vue.component(...)
c. 子组件: 3步
1). var 子组件对象名={
内容必须符合组件的要求
}
子组件对象名必须是驼峰命名
2). 父组件对象中:{
... ...
components{ 子组件对象名, ... ,... }
}
子组件对象名必须是驼峰命名
3). 父组件template中用<子组件标签名/>引入子组件内容
components会将子组件对象名的驼峰命名自动翻译为-分隔
所以, 使用子组件标签时,要用-分隔多个单词
(2). 组件间传参: 父给子
a. 父组件给:
<子组件 :自定义属性名="父组件变量">
b. 子组件取:
props:["自定义属性名"]
结果: 在子组件内,props中的"自定义属性名"与子组件自己data中的变量用法完全相同!
14. 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). 可将"自定义参数名"用作绑定或程序中都行
15. 安装vue脚手架:
(1). 设置npm默认使用国内淘宝镜像仓库:
npm config set registry http://registry.npm.taobao.org
备选: npm i -g cnpm --registry=http://registry.npm.taobao.org
(2). 安装可反复生成项目脚手架代码的工具:
npm i -g @vue/cli
备选: cnpm i -g @vue/cli
(3). npm run serve
启动vue脚手架项目
(4). 打开浏览器,地址栏输入: http://localhost:8080
16. 脚手架文件夹结构:
(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">位置。
17. 避免组件间样式冲突:
(1)<style scoped>
(2)<template>
<组件父元素 class="组件名">
</template>
<style>
.组件名>子元素选择器{ ... }
</style>
18. 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(()=>{
正式的查找操作
},等待时间)
}
19. 懒加载:
(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,不要异步下载暂时不需要的页面组件文件
},
}
【后文传送门】👉 vue中如何使用http代理解决跨域问题_11
如果这篇【文章】有帮助到你,希望可以给【青春木鱼】点个赞👍,创作不易,相比官方的陈述,我更喜欢用【通俗易懂】的文笔去讲解每一个知识点,如果有对【前端技术】感兴趣的小可爱,也欢迎关注❤️❤️❤️【青春木鱼】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】💕💕!