vue
一 : Vue 介绍
- vue 中文网
- github 下载地址
- Vue.js (读音 /vju:/ view)
- 渐进式 JavaScript 框架
3.1 渐进式 :
小型项目 就可以使用 vue 就高度了 随着页面的复杂程序提高,就要学习 vue-rouer 来管理更多的页面 再随着项目的数据越来越多,管理数据也变得麻烦起来了,就开始使用 vuex 来管理数据
3.2 框架 : 一整套的解决方案
框架和库的区别 (面试-->控制翻转)
1. 库(Library) , 代表 : jquery
- 库就是一系列函数的集合, 我们开发人员在使用库的时候,想要完成什么样的功能,就调用库中提供的某个方法
比如 : 想要添加样式, 就调用 jquery 中的 .css() / .addClass()
- 库起到了一个辅助的作用, 在使用库的是时候,是由开发人员说了算, 也是由开发人员起主导作用.
比如 : 想给 A:设置样式 A.css(), B:addClass() C:style.background='red'
####2. 框架 (Framework), 代表:vue
- 在使用框架的时候,是由框架说了算,由框架起到了主导作用,
- 框架是一套完整的解决方案,框架中制定了一套规则,使用框架的时候,只需要按照规则,把代码放到合适的地方,然后框架会在合适的时机,主动调用开发人员的代码
比如 : 想用vue,组件里遍历就得使用 v-for, 下次不用 v-for 了,使用 for 不行 v-for='item in list'
3. 主要区别 : 控制反转
也就是 : 谁起到了主导作用
- 使用库的时候 : 开发人员起主导作用
- 使用框架的时候:框架起到了主导作用
- 从体量上看,框架一般比库大
- 会发现使用框架的时候,会受到很多限制
- 我们所说的前端框架与库的区别?
三 : MVC + MVVM (面试)
第 3.1 MVC
- MVC 是一种软件架构模式,也有人叫做设计模式
- M : Model 数据模型 (专门用来操作数据,数据的 CRUD)
- V : View 视图 (对于前端来说,就是页面)
- C : Controller 控制器 (是视图和数据模型沟通的桥梁,用于处理业务逻辑)
第 3.2 MVVM
Vue 使用的是 MVVM 模式
- MVVM ===> M / V / VM
- M : model 数据层
- V : view 视图层
- VM : ViewModel 视图模型
- 核心 : M <===> VM <===> V
第 3.3 MVVM 优势
- MVC 模式 将应用程序划为三个部分,实现职责分离
- 但是,在前端中,经常要通过 js 代码来进行一些逻辑操作,最终还要把这些逻辑操作展示页面中, 也需要
频繁的操作DOM - 比如 : ajax 请求、添加、修改、设置样式、动画
- 但是,在前端中,经常要通过 js 代码来进行一些逻辑操作,最终还要把这些逻辑操作展示页面中, 也需要
- MVVM 提出来的思想 通过
数据双向绑定让数据自动的双向同步- V (修改视图) --> M
- M (修改数据) --> V
- 采用的是 : 数据驱动视图的思想, 数据是核心
- 以后如果想要操作 DOM, 立马想到的不是应该是拿到元素设置,而是数据
第 3.4 Vue 中的 MVVM
- 注意 : 不推荐直接手动操作 DOM
每个人操作 DOM 的方法不一样,会造成性能不一样 官网 : 虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm (ViewModel 的缩写) 这个变量名表示 Vue 实例。
第 3.5 学习 Vue 要转化思想
- 数据驱动视图 : 不要再想着怎么操作 DOM, 而是想着如何操作数据
四 : 起步 - Hello Vue
第 4.1 : 基本使用
- 安装 :
npm i vue(小写) - 导入 :
<script src='./vue.js'></script> - 实例 vue
const vm = new Vue({
// 指定 vue 管理的边界
el: '#app',
// 提供视图中 需要的数据
// 视图可以直接使用data中的数据
data: {
msg: 'xxx'
}
})
第 4.2 : 使用注意点
- vm 官网建议
- Vue 构造函数首字母大写
- 参数是一个对象
- id='#app', 其他也可以
- 边界外的无法使用 msg
4.3 {{}} 插值表达式
- {{}} Mustache 小胡子语法, 插值表达式
- 作用 : 使用
{{}}从data中获取数据,并展示在模板中 - 说明 :
{{}}中只能出现 js 表达式
- 表达式 (有返回值的)
- 常见的数据类型
1 'abc' false [] {} - 数据类型 和 运算符结合在一起
1+2 arr.join('-') true ? 123 : 321
- 常见的数据类型
- 语句
if语句 for语句
- {{}} 语法 不能作用在 HTML 元素的属性上
- 一般只有单标签dom元素不能够使用插值表达式
五 : 数据双向绑定演示
第 5.1 : 一个 input + v-model
v-model 指令 : 数据双向绑定的指令
作用 : 把data中的msg值 和 input上的值 绑定到一起
注意 : v-model只能用在 表单控件上 (input checkbox 等)
> 可以在浏览器分别演示 V => M 和 M =>V
第 5.2 : Object.defineProperty
let obj = {}
let temp
obj.name = 'zhanhgsan'
// 参数1 : 要给哪个对象设置属性
// 参数2 : 给对象设置什么属性
// 参数3 : 属性的修饰符
Object.defineProperty(obj, 'name', {
set: function(newVal) {
console.log('赋值了', newVal)
},
get: function() {
console.log('取值了')
return temp
}
})
第 5.3 : 数据双向绑定的原理
<input type="text" id="txt" />- 演示 : V ==> M
//1. 拿到文本框
const txt = document.getElementById('txt')
//2. 时刻监听txt ,拿到最新的值
txt.oninput = function() {
console.log(this.value)
obj.name = this.value
}
//3. 数据模型
var obj = {}
let temp
Object.defineProperty(obj, 'name', {
// 给属性赋值的时候会掉
set: function(newVal) {
console.log('调用了set', newVal)
temp = newVal
txt.value = newVal
},
get: function() {
console.log('调用了get')
return temp
}
})
- V => M
//1. 获取 input的值,最新值
//2. 通过监听oninput 拿到最新值
//3. 把最新值赋值给 obj.name
//4. 就会掉了set方法,就会修改了数据
- M => V
//1. obj.name = 'xxx'
//2. 调用 set方法
//3. 把值赋值给input元素
六 : 指令学习
第 6.0 指令
- 指令 : 就是一个特殊的标记, 起一个辅助作用, 使 html 具备原来没有的功能
- vue 中 所有的指令, 都是以 v-开头的
- 比如 : v-model v-bind v-if v-for 等等
第 6.1 v-model (常用)
说明 : 用在
表单元素中, 用来实现数据双向绑定(input checkbox 等等) 作用 : 将数据txt和文本框的值绑定到一起, 任何一方发生改变,都会引起对方的改变 注意 : v-model 在不同类型的表单元素中,该指令的作用不同
<!-- 文本输入框 绑定的是值 -->
<input type="text" v-model="txt" />
<!-- 多选框、按钮 绑定的选中状态 -->
<input type="checkbox" v-model="isChecked" />
第 6.2 v-text 和 v-html
说明 : 设置文本内容
- v-text : 相当于之前的 innerText , 设置文本内容(不识别 html 标签) == 标签内部{{}} ===》注意v-text会完全替换掉标签里的内容
- v-html : 相当于之前的 innerHTML , 设置文本内容(识别 html 标签)
- 数据
msg1: '<a>这是一条信息</a>',
msg2: '<a href="#">我也是一条信息</a>'
第 6.3 v-bind (常用)
说明 : 动态绑定数据 (单向) 出现原因 : 在 HTML 属性中, 无法使用插值表达式 步骤:
// 加载静态数据
<a href="https://wbaidu.com">aaa</a>
// 想加载动态数据 {{ }}可以获取data中的数据
// 但是 {{}} 不能使用在属性中
<a href="{{ src }}">aaa</a>
// 所以使用v-bind
<a v-bind:href="{{ src }}">aaa</a> ok
<img v-bind:src="src"> ok
缩略 : v-bind 全部省略 只留下一个
:改为 :
<a :href="src">aaa</a> ok
<img :src="src" /> ok
以后 使用 静态加载数据 :
<a href="https://wbaidu.com">aaa</a>动态加载数据 :<a :href="href">aaa</a>
第 6.3.1 v-bind 和 v-model 的区别
// v-model : 数据双向绑定 表单元素上
// : : 动态绑定数据(单向) 任意元素动态读取属性
容易出错点 :
<!-- v-model 数据双向绑定 -->
<!--场景 : 表单元素中 -->
<input type="checkbox" v-model="isChecked1" /> <br />
<!-- v-bind 数据动态绑定 (单向) -->
<!--场景 : 主要用在属性中 -->
<input type="checkbox" :checked="isChecked2" />*
第 6.4 操作样式
1.操作样式
<!-- 1. 静态类名 -->
<h1 class="red">哈哈</h1>
<!-- 2. 动态类名 -->
<h1 :class="cls">哈哈</h1>
<!-- 3. 最常用 -->
<!-- :class 里的值是一个对象
键 : 类名 (可能会要,也可以不要)
值 : 布尔值, true/false 确定类要不要
-->
<h1 :class="{ red:isRed , fz:isFZ}">哈哈</h1>
<!-- 4. style -->
<h1 :style="{ color : 'red', fontSize : fz + 'px' }">哈哈</h1>
2.其他操作
<!-- 1 -->
<!-- 重点 -->
<div :class="{ active: true }"></div>
===>
<div class="active"></div>
<!-- 2 -->
<div :class="['active', 'text-danger']"></div>
===>
<div class="active text-danger"></div>
<!-- 3 -->
<div :class="[{ active: true }, errorClass]"></div>
===>
<div class="active text-danger"></div>
--- style ---
<!-- 1 -->
<h1 :style="{ color : 'red', fontSize : fz + 'px' }">哈哈</h1>
<!-- fz : 80 -->
<!-- 2 将多个 样式对象 应用到一个元素上-->
<!-- baseStyles 和 overridingStyles 都是对象 -->
<!-- 如果有相同的样式,以后面的样式为准 -->
<div :style="[baseStyles, overridingStyles]"></div>
第 6.5 v-on 指令
注册事件/绑定事件
-
v-on:click 绑定了一个单击事件
: 后面是事件名称
<button v-on:click="fn">按钮</button>
-
缩写 : @click='fn'
<button @click='fn'>按钮</button> -
函数写在
methods里面
fn : function(){ ... }
fn() { ... }
- 函数里面的 this 指的就是 vm 实例
> 在函数里面操作数据
- 获取数据 this.msg
- 修改数据 this.msg = 'XXX'
- 传参 5.1 正常传参
@click='fn(123)'
5.2 事件对象 $event
<!-- 4.1 绑定事件对象的时候, 没有添加小括号,此时,直接在方法中,通过参数 e 就可以获取到事件对象 -->
<!-- 4.2 如果添加小括号,就拿不到事件对象了,vue留了一个$event -->
<button @click="fn1($event,123)">我是按钮</button>
6.5 v-for
- v-for 指令: 遍历数据,为数据中的每一项生成一个指令所在的标签
- 代码
<!-- 需求1 : 最常用 遍历数组 -->
<!-- 作用 : 遍历 list1 数据, 为数据中的每一项生成一个li标签 -->
<!-- item 数组里的每一项元素 -->
<ul>
<li v-for="(item,index) in list1">{{ item }} - {{ index }}</li>
</ul>
<!-- 需求2 : 遍历元素是对象的数组 -->
<ul>
<li v-for="item in list2">{{ item.name }} - id:{{ item.id }}</li>
</ul>
<!-- 需求3 : 遍历对象 -->
<ul>
<li v-for="(item,key) in obj">{{ item }}-{{key}}</li>
</ul>
<!-- 需求4 : 想要生成10个h1 -->
<h1 v-for="item in 10">我是h1 {{ item }}</h1>
-
重点是遍历数组,,其他的了解即可
注意用v-for更新已经渲染过的元素列表的时候,他会默认就地复用策略,如果数组的顺序发生改变,vue不会一定dom元素来匹配顺序,而是简单的复用此处的每个元素,这样每个节点的状态就会对应不上
解决这时候就需要给每个列表元素提供一个
key,这个key需要是唯一的,以保证vue能准确的追踪到每个节点,key最好是列表中每项的id
七 : TODOMVC
###一 : 准备工作
-
演示效果 :
-
当前任务:敲代码、视频、游戏
-
git clone https://github.com/tastejs/todomvc-app-template.git -
安装依赖包 :
npm i
二 : 配置 vue
-
安装 vue :
npm i vue -
导入 vue :
<script src="./node_modules//vue/dist/vue.js"></script>在
index.html里的app.js导入之前导入,因为 app.js 里 就要用到 vue 了 -
实例化 vue :在
app.js中创建 vue 示例,并设置好边界el:'#app'找到 index.html ,给 section 标签添加一个 id
-
测试 vue :
data 中随便来一个 msg 看能不能显示到视图中
生命周期函数
- 所有的 vue 组件,都是 vue 实例, 一个组件对应一个实例,并且接收相同的选项对象(一些根实例特有的选项除外)
- 实例生命周期也叫做 : 组件生命周期
这里先甩一张图

生命周期介绍
- 简单说 : 一个组件(实例) 从开始到最后消灭所经历的各种状态,就是一个组件(实例)的生命周期
- 生命周期钩子函数的定义 : 从组件被创建,到组件挂在到页面上运行,再到页面关闭组件被销毁,这三个阶段总是伴随着组件各种的事件,这些事件,统称为组件的生命周期函数 (简称 : 钩子函数)
- 开发人员可以通过 vue 提供的钩子函数,让我们写的代码参与到 vue 的生命周期里面来,让我们的代码在合适的阶段起到相应的作用
#####注意 :
- 注意 : vue 在执行过程中 会自动调用
生命周期钩子函数, 我们只需要提供这些钩子函数即可 - 注意 : 钩子函数的名称都是 vue 中规定好的
学习 vue 组件生命周期 学什么?
- Vue 内部执行的流程(难)
- 钩子函数如何使用 (两个重要的钩子函数 created mounted)
钩子函数 - beforeCreate
-
说明 : 在实例初始化之前,数据观测 和 event/watcher 事件配置之前被调用
-
组件实例刚被创建,组件属性计算之前, 例如 data 属性 methods 属性
-
注意 : 此时,无法获取 data 中的数据 和 methoids 中的方法
-
场景 : 几乎不用
钩子函数 - created (掌握)
-
说明 : 组件实例创建完成,属性已绑定, 可以调用 methods 中的方法、可以获取 data 值
-
使用场景 : 1-发送 ajax 2-本地存储获取数据
-
beforeCreate() { // 无法获取数据和事件 console.warn('beforeCreate', this.msg, this.fn) }, created() { console.warn('created', this.msg, this.fn) },
Has 'el' option ?
- YES => 就是正常的 el 边界
- NO => 可以注释,但是必须要手动添加
vm.$mount(el)去指定边界
vm.$mount('#app')
Has template option?
- No => 将 el 的 outerHtml 作为模板进行编译 ( outerHTML = 自身 + innerHTML )
- YES =>
// 如果提供了 template, 那么 vue 就会将 template 的内容进行编译,编译后,替换页面中 vue 管理的边界
template : `
<h1>嘻嘻</h1>
`,
钩子函数 - beforeMounted()
- 说明 : 在挂载开始之前被调用 (挂载:可以理解DOM 渲染)
钩子函数 - mounted() (掌握)
-
说明 : 挂载之后, DOM 完成渲染
-
使用场景 : 1-发送 ajax 2-操作 DOM
-
记得把template去掉 // 渲染DOM之前 beforeMount() { // 渲染之前的 <h1 id="h1" @click="fn">{{ msg }}</h1> console.log(document.getElementById('h1')) }, // 渲染DOM之后 <h1 id="h1">测试</h1> mounted() { console.log(document.getElementById('h1')) }
钩子函数 - beforeUpdated()
-
说明:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
-
注意:此处获取的数据是更新后的数据,但是获取页面中的 DOM 元素是更新之前的
小提示 : 打印 this.$el ,打开小三角是之后的,是因为打印是有监听的功能,展示的是后面更改之后的
钩子函数 - updated()
-
说明:组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
-
beforeUpdate() { // 更新之前的值 : 信息 console.warn('beforeUpdate', document.getElementById('h1').innerText) }, updated() { // 更新之后的值 : 信息1111 console.warn('updated', document.getElementById('h1').innerText) }
钩子函数 - beforeDestroy()
- 说明:实例销毁之前调用。在这一步,实例仍然完全可用。
- 使用场景:实例销毁之前,执行清理任务,比如:清除定时器等
created() {
this.timerId = setInterval(() => {
console.log(1111);
}, 500);
},
// 如果当组件销毁了,还不清除定时器会出现性能问题
// 在浏览器中可以尝试销毁 vm.$destroy()
// 最后销毁
beforeDestroy() {
clearInterval(this.timerId)
},
钩子函数 - destroyed()
说明:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
使用钩子函数来完善 数据存储
created {
this.list = localStorage.getItem('list')
}
使用接口的形式发送数据
一 : json-server 提供假数据接口
-
json-server 作用 : 根据指定的 JSON 文件, 提供假数据接口
-
地址 : json-server
-
使用步骤
1. 全局安装 json-server : `npm i -g json-server` 2. 准备一个json数据 3. 执行 : `json-server data.json` > json数据参考 json数据可以参考 : { "todos": [ { "id": 1, "name": "吃饭", "age": 20 } ] } -
REST API 格式
* 1. 查询 : GET
* 2. 添加 : POST
* 3. 删除 : DELETE
* 4. 更新 : PUT 或者 PATCH(打补丁)
- 具体接口
* 1. 查询全部数据 http://localhost:3000/todos
* 查询指定数据 http://localhost:3000/todos/2
*
* 2. 添加一个对象 //localhost:3000/todos
* POST
* id 会自动帮我们添加
*
* 3. 更新数据 http://localhost:3000/todos/3
* PUT 或者 PATCH
* PUT 需要提供该对象的所有数据
* PATCH 只需要提供要修改的数据即可
*
* 4. 删除数据
* http://localhost:3000/todos/3
* DELETE
- 可以借助
postman测试接口;
二 : axios 发送请求 (重点掌握)
读音 : /艾克笑丝/
-
作用 : 一个专门用来发送 ajax 请求的库, 可以在浏览器或者 node.js 中使用
Promise based HTTP client for the browser and node.js 以Promise为基础的HTTP客户端,适用于:浏览器和node.js 封装ajax,用来发送请求,异步获取数据 -
使用步骤
1. 本地安装 axios : `npm i axios` 2. 导入 axios 3. 使用 -
GTE 方式发送请求
// 方式1 axios.get('http://localhost:3000/todos/3').then(res => { console.log('获取到数据了:', res.data) }) // 方式2 axios .get('http://localhost:3000/menus', { params: { id: 003 } }) .then(res => { console.log('获取到数据了:', res.data) })5、 POST 方式发送请求
// post 请求 axios // 第一个参数:表示接口地址 // 第二个参数:表示接口需要的参数 .post('http://localhost:3000/todos', { name: 'axios添加数据', done: true }).then ( res => 打印 res) -
使用axios测试 todos 的几种接口
//1. GET
// 没有参数
axios.get('http://localhost:3000/todo').then(res => {
console.log(res)
})
// 有参数
axios.get('http://localhost:3000/todo/1').then(res => {
console.log(res)
})
axios
.get('http://localhost:3000/todo', {
params: {
id: 2
}
})
.then(res => {
console.log(res)
})
// 2. POST
axios
.post('http://localhost:3000/todo', {
name: 'XXX',
age: 1
})
.then(res => {
console.log(res)
})
//3. 删除
axios.delete('http://localhost:3000/todo/8', {}).then(res => {
console.log(res)
})
//4. 更新 - put
axios
.put('http://localhost:3000/todo/2', {
name: '111',
age: 300
})
.then(res => {
console.log(res)
})
// 4. 更新 - patch
axios
.patch('http://localhost:3000/todo/1', {
age: 3000
})
.then(res => {
console.log(res)
})
三 : 完善 TodoMVC
-
获取全部
axios.get('http://localhost:3000/list').then(res => { console.log(res.data); this.list = res.data; }); -
删除任务
axios.delete('http://localhost:3000/list/' + id).then(res => { this.list = this.list.filter(item => item.id !== id); console.log(res); }); -
添加任务
# id 会自动加 axios .post('http://localhost:3000/list', { name: todoName, done: false }) .then(res => { console.log(res.data); this.list.push({ // 属性名 和属性值 一样的话,,可以省略一个 id, name: todoName, done: false }); }); -
更新任务 (在 hideEdit 里实现)
# 难点1 : 在 hideEdit 这个方法里 # 难点2 : 获取的这个id 就是editId hideEdit(e) { axios.patch('http://localhost:3000/list/' + this.editId, { name: e.target.value }).then(res => { console.log(res); this.editId = -1; }) }, -
删除已经完成 的
# 因为 这个假接口没有实现以下删除 完毕 clearCompleted() { // 清除已经完成的,,只需要过滤出来未完成的即可 let arr = this.list.filter(item => item.done); arr = arr.map(item => item.id); for (let i = 0; i < arr.length; i++) { axios.delete('http://localhost:3000/list/' + arr[i]).then(res => { this.list = this.list.filter(item => item.id != arr[i]); }); } }, -
点击修改状态
# 单独给 checkbox 都注册点击事件 clickBox(id) { let todo = this.list.find(item => item.id == id); axios.patch('http://localhost:3000/list/' + id, { done: !todo.done }).then(res => { console.log(res); }); }
单页面应用程序
- SPA : Single Page Application
- MPA : Multiple Page Application 多页面应用程序
单页web应用,
就是只有一个web页面的应用,
是加载单个HTML页面,
并在用户与应用程序交互时动态更新该页面的web应用程序
- 区别
对于传统的多页面应用程序来说, 每次请求服务器返回的都是一个完整的页面
对于单页应用程序来说,
只有第一次会加载页面,
以后的每次请求,
仅仅是获取必要的数据.然后,
由页面中js解析获取的数据,
展示在页面中
- 单页面优势 :
- 减少了请求体积,加快页面响应速度,降低了对服务器的压力
- 更好的用户体验,让用户在 web app 感受 native app 的流畅, (局部刷新)
- 单页面劣势 :
- 开发成本高
- 不利于SEO
- 演示 : music.163.com/
组件 (重难点)
一 : 组件化开发
- 概念 :将一个完整的页面,抽离成一个个独立的组件,最终,通过这一个个独立组件完成整个的页面(项目)的功能
- 组件化开发的优势/作用 : 复用
- 官网 : 组件是可复用的 Vue 实例
二 : 组件的基本使用
-
Vue 中的两种注册组件的方法 1.全局注册 2.局部注册
全局组件在所有的vue实例中都可以使用 局部组件在所有的当前实例中可以使用
- 注册全局组件 - 基本使用 1
/**
* 第一个参数 : 组件名
* 第二个参数 : 是一个配置对象, 该配置对象与 Vue 实例的配置对象几乎完全相同
* 也就是说: vue实例中用到的配置项,和组件中的配置项几乎相同
*/
Vue.component('hello', {
template: `
<h1 class="red">这是hello组件</h1>
`
})
- 注意点
- 注册全局组件也是放到 vm 实例之前
- 模板只有一个根节点
- 组件的配置项和 vue 实例 的配置项一样 (如:data、methods、filters、watch、computed、钩子函数等)
- 组件的 data 是一个函数 返回一个对象
var Component = function() {}
// 使用对象
Component.prototype.data = {
demo: 123
}
// 使用函数
Component.prototype.data = function() {
return {
demo: 111
}
}
var component1 = new Component()
var component2 = new Component()
component1.data().demo = '8888'
console.log(component2.data().demo) // 456
示例:
<h1 @click='fn' class="red">这是hello组件</h1>
methods: {
fn() {
console.log('fn')
}
},
computed: {},
watch: {},
filters: {}
}
三: 改造 TodoMVC 成 组件化结构
- 安装 vue + 实例化
- 创建一个 components 文件夹
- 具体步骤
- 创建一个
todo-head.js - 导入
<script src="./components/todo-head.js"></script> - 使用
<todo-head></todo-head>
四 : 组件通讯 (介绍)
- 组件是一个独立、封闭的个体
- 也就是说 : 组件中的数据默认情况下, 只能在组件内部使用,无法直接在组件外部使用
- 可以将 vue 实例看做一个组件
- 对于组件之间需要相互使用彼此的情况,应该使用 组件通讯 机制来解决
- 组件通讯的三种情况 :
- 父组件将数据传递给子组件(父 -> 子)
- 子组件将数据传递给父组件 (子 => 父)
- 非父子组件(兄弟组件)
五 : 父 ==> 子 (重点) 两步
- 将要传递的数据,通过属性传递给子组件
<child :msg="pmsg"></child>
- 子组件通过 props 配置项,来指定要接收的数据
props: ['msg']
完善 TodoMVC => 完成 传值 + 渲染列表页
六 : 子 ==> 父 (重点) 三步
1 父组件中提供一个方法
pfn(arg) {
console.log('父组件中接受到子组件传递过来的数据:', arg)
}
2 将这个方法传递给子组件
// 自定义事件 <child @fn="pfn"></child>
3 子组件调用这个方法( 触发父组件中传递过来的自定义事件 )
// 这个方法是点击事件触发的
handleClick() {
// 调用父组件中的方法 fn
// 注意:通过 $emit 方法来触发事件 fn
// 第一个参数:表示要触发的自定义事件名称,也就是 @fn
// 第二个参数:表示要传递给父组件的数据
this.$emit('fn', 'child msg')
}
完善 TodoMVC => 完成 传值 +添加+ 删除+清除完成
七 : 目前为止存属性的地方
- data
- 计算属性
- props
八 : 单向数据流(组件与组件之间) (了解)
所有的 prop 都使得其父子 prop 之间形成了一个
单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。 看图 当 todo-head 中的 todoName 设置数据后回车添加到 todoList,todoList 的长度就会发生变化, 然后就会根据(组件与组件之间的)单向数据流,把数据单向下流到子组件中 而且必须是通过 props 往下传递的才可以
props 的特点 : 只读
-
验证 props 只读
-
正常下是不需要子组件修改父组件传过来的值的,但是.....
-
修改
父组件传给子组件的数据思路 : 把接收过来的数据,保存到 data 中一个临时值
Vue.component('child', {
template: `
<div>子组件 {{ cmsg }} </div>
`,
data() {
return {
cmsg: this.msg
}
},
props: ['msg'],
created() {
this.cmsg = 666
}
})
prop 的大小写
-
官 : HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。
html 的标签和 属性 都是一样,忽略大小写
<H1 TITLE="哈哈">我是h1</H1> -
官 : 这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名不好使了
<child :cMsg="pmsg"></child>会报警告,父传子也接收不到了- 原因是 : 接收的属性是:cMsg, 接收的数据名,因为忽略大小写,数据已为 : cmsg
- 所以已经准备要读取的 是 cmsg 的值,找不到,所以要报警告
You should probably use "c-msg" instead of "cMsg".
-
方式 1 : 全用小写,不要使用驼峰命名
接收 :
cmsgprops/读取 :cmsg -
方式 2 官 : 需要使用其等价的 kebab-case (短横线分隔命名) 命名:
接收 :
:c-msg='pmsg'props/读取 :cMsg
九 : 完善 TodoMVC : 底部隐藏+剩余完成数+清除完成
计算属性 : 已知值(todoList 在 根组件) ==> 得到一个新值(子组件里使用)
父 => 子通讯
十 : 非父子之间通讯 ( 组件 => 组件 ) (重点)
需求 : 组件 jack ===> 恁弄啥哩 ===> 组件 rose
- 是通过
事件总线 (event bus 公交车) 的机制来实现的 - 事件总线 : 实际上就是一个
空Vue实例 - 可以实现任意两个组件之间的通讯,而不管两个组件到底有什么样的层级关系
- 看图
- 示例 :
// 第一步 : 事件总线
var bus = new Vue()
// 第二步 : 发送数据 可在点击事件里 触发事件
// 参数1 : 唯一标识 参数2:参数
bus.$emit('todo', '恁弄啥哩?')
// 第三步 : 接收数据 可在 created 里 注册事件
bus.$on('todo', arg => {
console.log('接收过来的', arg)
})
十二 : 注册局部组件
- 局部组件只能在当前父组件的模板中使用
- 不能再其他子组件里的子组件里显示
- 示例 :
// 注册局部组件:
components: {
// child表示组件名称
// 值为配置对象,与 Vue.component 中的第二个参数相同
// 注意:子组件child属于 Vue实例,因此,只能在 Vue实例的模板中使用
child: {
template: `
<div>这是局部组件 child</div>
`
}
}
十三 : 获取组件(或DOM元素) - refs
-
说明 :
vm.$refs一个对象, 持有已注册过 ref 的所有子组件 ( HTML 元素) -
使用 :
- 注册
// 标签 <div ref="div">哈哈</div> // 组件 <child ref="child"></child>- js 中使用
// mounted 操作DOM * this.$refs.div * this.$refs.child -
注意点 : 如果获取的是一个子组件,那么通过 ref 就能获取到子组件中的
data和methodsthis.$refs.child.num this.$refs.child.fn -
场景 : 一般在第三方的组件中, 可能会用到这个功能
-
示例 :
// 组件
<div ref="div">哈哈</div>
<child ref="child"></child>
// js
mounted() {
console.log(this.$refs.div)
console.log(this.$refs.child.fn)
}
单页面应用与路由
单页面应用程序
- SPA : Single Page Application
- MPA : Multiple Page Application 多页面应用程序
单页web应用,
就是只有一个web页面的应用,
是加载单个HTML页面,
并在用户与应用程序交互时动态更新该页面的web应用程序
- 区别
对于传统的多页面应用程序来说, 每次请求服务器返回的都是一个完整的页面
对于单页应用程序来说,
只有第一次会加载页面,
以后的每次请求,
仅仅是获取必要的数据.然后,
由页面中js解析获取的数据,
展示在页面中
- 单页面优势 :
- 减少了请求体积,加快页面响应速度,降低了对服务器的压力
- 更好的用户体验,让用户在 web app 感受 native app 的流畅, (局部刷新)
- 单页面劣势 :
- 开发成本高
- 不利于SEO
- 演示 : music.163.com/
路由
- 路由 : 是浏览器 URL 中的
哈希值( # hash) 与展示视图内容之间的对应规则
在 web App 中, 通过一个页面来展示和管理整个应用的功能.
SPA 往往是功能复杂的应用,为了有效管理所有视图内容,前端路由 应运而生. (为什么要学习路由??)
简单来说,路由就是一套映射规则(一对一的对应规则), 由开发人员制定规则.
当 URL 中的哈希值( `#` hash) 发生改变后,路由会根据制定好的规则, 展示对应的视图内容
- vue 中的路由 : 是 hash 和 component 的对应关系, 一个哈希值对应一个组件
基本使用
-
- 安装路由 :
npm i vue-router
- 安装路由 :
-
- 引入路由
<script src="./vue.js"></script>
// 千万注意 :引入路由一定要在引入vue之后,因为vue-router是基于vue工作的
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
-
- 详细使用步骤
-
3.0 实例路由对象+挂载到 vue 上
const router = new VueRouter() 路由实例 与 Vue 实例 关联到一起 验证路由是否挂载成功, 就看打开页面,最后面有没有个
#/ -
3.1 入口 (#哈希值)
// 方式1 : url地址为入口 调试开发用
输入url地址 改变哈希值
`01-路由的基本使用.html#/one`
// 方式2 : router-link+to
- 3.2 设置路由规则
// path : 路由路径
// component : 将来要展示的路由组件
routes: [{ path: '/one', component: One }, { path: '/two', component: Two }]
-
3.3 组件
一个哈希值对应一个组件
const One = Vue.component('one', {
template: `
<div> 子组件 one </div>
`
})
- 3.4 出口
<!-- 出口 组件要展示的地方-->
<router-view></router-view>
- 分析执行过程(看图)
使用注意事项
- 入口(哈希)
- 最常用的入口
<!-- 1. 入口 -->
<!--
to 属性 , 实际上就是哈希值,将来要参与路由规则中进行与组件匹配
router-link 组件最终渲染为 : a标签, to属性转化为 a标签的href属性
-->
<router-link to="/one">首页</router-link>
-
组件
组件可以改为对象格式
const One = {
template: `
<div> 子组件 one </div>
`
}
-
过程 : 入口 ==> 路由规则 ==> 组件 ==> 出口
-
演示 : 多个组件匹配
可以有多个入口,但只需要一个出口
-
示例 :
<div id="app">
<!-- 1 路由入口:链接导航 -->
<router-link to="/one">One</router-link>
<router-link to="/two">Two</router-link>
<!-- 4 路由出口:用来展示匹配路由视图内容 -->
<router-view></router-view>
</div>
<!-- 导入 vue.js -->
<script src="./vue.js"></script>
<!-- 导入 路由文件 -->
<script src="./node_modules/vue-router/dist/vue-router.js"></script>
<script>
// 3 创建两个组件
const One = Vue.component('one', {
template: '<h1>这是 one 组件</h1>'
})
const Two = Vue.component('two', {
template: '<h1>这是 two 组件</h1>'
})
// 0 创建路由对象
const router = new VueRouter({
// 2. 路由规则
routes: [
{ path: '/one', component: One },
{ path: '/two', component: Two }
]
})
const vm = new Vue({
el: '#app',
//0. 不要忘记,将路由与vue实例关联到一起!
router
})
</script>
复习一遍代码 + 流程
入口导航菜单高亮处理
- 点击导航发现代码有两个类 :
<a href="#/one" class="router-link-exact-active router-link-active">One</a>
<a href="#/two" class="">Two</a>
- 修改方式 1 : 直接修改类的内容
.router-link-exact-active,
.router-link-active {
color: red;
font-size: 50px;
}
-
修饰方式 2 : 修改默认高亮类名 (推荐)
因为有时候项目中可能已经针对导航有过设置样式,后来再加的路由,所以如果再重新像方式 1 再加一次,会重复 所以 :可以 修改默认高亮的 a 标签的类名
const router = new VueRouter({
routes: [],
// 修改默认高亮的a标签的类名
// red 是已经存在过的
linkActiveClass: 'red'
})
精确匹配和模糊匹配
- 精确匹配 : router-link-exact-active 类名 : 只有当
浏览器地址栏中的哈希值 与 router-link 的 to 属性值,完全匹配对,才会添加该类 - 模糊匹配: router-link-active 类名 : 只要
浏览器地址栏中的哈希值包含 router-link 的 to 属性值,就会添加该类名 - 解决办法 : 加个 exact
<router-link to="/" exact>
One
</router-link>
- 注意 : 精确匹配和模糊匹配,只对添加类名这个机制有效,与路由的匹配规则无关!!!
路由参数
看图分析: 列表 => 详情组件
- 导航入口
<!-- 1. 入口 -->
<router-link to="/detail/1">手机1</router-link>
<router-link to="/detail/2">手机2</router-link>
<router-link to="/detail/3">手机3</router-link>
- 传参: 最笨的方式
routes: [
// 2 . 路由规则
{ path: '/detail/1', component: Detail },
{ path: '/detail/2', component: Detail },
{ path: '/detail/3', component: Detail }
]
//3. 组件
const Detail = Vue.component('detail', {
template: `
<div> 显示详情页内容....{{ $route.path }} </div>
`
})
- 正确的方式 : 把传过去的 1/2/3 当成参数 3.1 路由规则的正确方式 :
routes: [
// 2 . 路由规则
{ path: '/detail/:id', component: Detail }
]
3.2 获取参数的三种正确方式
const Detail = Vue.component('detail', {
template: `
// 方式1 : 组件中直接读取
<div> 显示详情页内容....{{ $route.params.id }} </div>
`,
created() {
// 方式2 : js直接读取
// 打印只会打印一次,因为组件是复用的,每次进来钩子函数只会执行一次
console.log(this.$route.params.id)
},
// 方式3 : 监听路由的参数变化 为什么不需要深度监听,因为它不是data中的
watch: {
$route(to, from) {
console.log(to.params.id)
}
}
})
-
如果输入的没有带参数的
有参数的对应 : 下面都是正确打开方式
detail/1 ==> 显示详情页内容....1
detail/2 ==> 显示详情页内容....2
detail/3 ==> 显示详情页内容....3
如果通过改变 url 地址,不输入参数 就看不到
显示详情页内容这些字 说明没有正确匹配到detail组件内
- 有可能不输入参数 :后面加个? 代表可传可不传
- ** { path: '/detail/:id?', component: Detail }**
// 可识别的路径是这样的
* detail
* detial/1
* detial/2
* detial/3
- 示例 :
// 链接:
<router-link to="/user/1001">用户 Jack</router-link>
<router-link to="/user/1002">用户 Rose</router-link>
// 路由:
{ path: '/user/:id', component: User }
// User组件:
const User = {
template: `<div>User {{ $route.params.id }}</div>`
}
重定向
- 解释:将 / 重定向到 /home
{ path: '/', redirect: '/home' }
子组件 (项目中讲)
Webpack
概念 ==> webpack 是什么?? 前端模块化打包(构建)工具
webpack可以解决这些依赖关系,并对他们进行打包
webpack 的两个方面
1 - 打包 2 - 模块化
一、打包 : 前端打包(构建)都能做什么??
-
语法转换
- Less/SASS 预编译CSS -> CSS -> 浏览器中使用 - ES6 新语法有兼容性问题 -> ES5 -> 浏览器中使用 - const fn = () => {} ===> var fn = function() {} -
文件压缩、合并
JS/HTML/CSS 压缩后,才能发布上线 文件合并( 不管有多个JS、CSS,默认打包直接生成一个JS文件 ) -
开发期间提供一个服务器环境
自动打开浏览器、监视文件变化,自动刷新浏览器 -
项目上线,打包后才能上线
-
总结
webpack 这个打包(构建)工具,就是提供了前端开发需要的一整套完整的流程,也就是 webpack 能够渗透的前端开发的各个环节、各个流程中,帮你实现 复杂、繁琐、重复的工作,有了 webpack 后,开发人员只需要关注当前要实现的业务即可。
二、模块化功能
-
模块化 : 逻辑
-
组件化 : 界面
-
webpack为前端提供了模块化开发环境,而且webpack是基于node,有了webpack,就可以像写Node代码一样,写前端代码了
-
在 webpack 看来所有的资源都是模块,不管是: CSS、图片、字体、JS、html 等
-
在webpack提供的模块化环境中, 1 想要加载一个JS文件,只需要 require('./a.js') 2 想要加载一个CSS文件,只需要 require('../css/index.css') 3 想要加载一个图片文件,只需要 require('../iamges/a.png') 4 ...
Webpack 四个核心概念:
入口(entry)、出口(output)、加载器(loader)、插件(plugins)
- 入口 : 要打包哪个文件
- 出口 : 要打包到哪里
- 加载器 : 加载除了js文件其他文件的功能
- 插件 : 处理加载器完成不了的功能, 使用插件
学习 手动配置 webpack 的目的
- 为了我们后面使用脚手架做准备,
- 能够完成webpack的基本安装
- 能够了解webpack配置文件的作用
- 能够说出webpack中loader 的作用
- 能够使用webpack处理常见的资源(css/less/图片)
- 能够说出webpack-dev-server的作用以及配置
- 目标 : 能跟着 视频 + 笔记 照着敲完一遍即可
webpack 使用步骤 (最新版)
第一阶段
命名初始化阶段
文件名不能有汉字,不能取名叫 webpack
- 生成
package.json, 命令 :npm init -y - 安装 :
npm i -D webpack webpack-cli
webpack : 是 webpack 工具的核心包
webpack-cli : 提供了一些在终端中使用的命令
-D(--save-dev) : 表示项目开发期间的依赖,也就是 : 线上代码中用不到这些包了
- 创建一个
main.js文件
console.log('我就要被打包了,哦也');
- 在
package.json的scripts中,添加脚本
"scripts": {
"build": "webpack main.js"
},
// webpack 是webpack-cli 中提供的命令, 用来实现打包的
// ./main.js 入口文件,要打包哪个文件
- 运行 :
npm run build - 设置开发状态 :
mode
"build" : "webpack ./main.js --mode development"
// WARNING in configuration
// The 'mode' option has not been set, webpack will fallback to 'production' for this value.
// 如果没有设置 mode 配置项, webpack 会默认提供 开发环境(production)
// Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
// 项目开发的两种环境
1. 开发环境 (development) : 开发过程就是开发环境
2. 生产环境 (production) : 线上环境, 也就是 : 项目做好了,发布上线
生产环境下, 打包生产的js文件都是压缩后的, 开发环境下代码一般是不压缩的
第一阶段 隔行变色案例
- 创建
src/index.html - 隔行案例 =>
html => ul#list>li{我是 li \$}\*10 - 安装 juqery :
npm i jquery, 并且引入jquery - 暂时引入
main.js, 在 main.js 里写入
// 使用ES6的模块化语法
import $ from 'jquery' // 优点 不用沿着路径去找
$('#list > li:odd').css('backgroundColor', 'red')
$('#list > li:even').css('backgroundColor', 'green')
// 语法报错
- 问题 :
// 引入 main.js 会报错,因为浏览器不支持这个import 的Es6语法
// npm run build 之后
// 引入 dist/main.js 后会ok,因为webpack 帮我们转化为浏览器能够识别的es5语法了
- 历程 :
//1. 如果index.html 中引入 jquery , 再引入 mian.js (没有引入jquery了) => ok
//2. 如果index.html 中没有引入 jquery , 直接使用es6的模块化语法 import , 引入main.js就会报错
//3. 如果index.html 中没有引入 jquery , 直接使用es6的模块化语法 import , webapck打包出 dist/main.js 引入 dist/main.js ==> ok
-
为什么
dist文件下的main.js文件里的代码突然这么多看图 (打包流程)
-
code 记得保存一份
第二阶段
配置文件
-
准备工作 :
src源文件 :index.html和main.js -
webpack 打包有两种方式
1-命令行 2-配置项
-
方式 1 : 命令行
* "build" : "webpack ./mian.js" --mode development
* npm run build
* 一般情况下 : 改为
* "build" : 入口 --output 出口
* "build": "webpack ./src/js/main.js --output ./dist/bundle.js --mode development",
*
* 验证 : index.html 引入 bundle.js
- 方式 2 : 配置项
第一步 : 项目`根目录`下, 创建一个 `webpack.config.js`文件 (文件名固定死)
专门指定其他文件 : --config webpack.XX.js
第二步 : 在 `webpack.config.js` 中,进行配置
// webpack 是基于 node的 , 所以配置文件符合 node 方式书写配置
// 注意 : 不要再这个文件中使用ES6的的模块化 import语法
// main.js里可以使用,是因为要通过webpack转化为es5的
// 而这个是webpack 的配置文件,,它是要转化别人的,所以必须要通过
第三步 : 修改 `package.json` 中的 `scripts` , 脚本命令为: "build": "webpack"
第四步 : 执行命令 : `npm run build`
- 示例代码(webpack.config.js) :
const path = require('path')
module.exports = {
// 入口
entry: path.join(__dirname, './src/js/main.js'),
// 出口
output: {
// 出口目录
path: path.join(__dirname, './dist'),
filename: 'bundle.js'
},
// 开发模式
mode: 'development'
}
配置文件 html-webpack-plugin
- html-webpack-plugin 必备的插件
作用 :
1. 能够根据指定的模板文件 (index.html),自动生成一个新的 index.html,并且注入到dist文件夹下
2. 能够自动引入js文件
- 安装 :
npm i html-webpack-plugin - 配置 :
第一步: 引入模块
const htmlWebpackPlugin = require('html-webpack-plugin')
第二步: 配置
// 插件
plugins: [
// 使用插件 指定模板
new htmlWebpackPlugin({
template: path.join(__dirname, './src/index.html')
})
]
配置文件 : webpack-dev-server
- webpack-dev-server 使用 webpack 必备的功能(插件)
作用 : 为使用 webpack 打包提供一个服务器环境
- 1.1 自动为我们的项目创建一个服务器
- 1.2 自动打开浏览器
- 1.3 自动监视文件变化,自动刷新浏览器...
- 使用步骤 :
- 2.1 安装 :
npm i -D webpack-dev-server - 2.2 两种使用方式: 1-命令行 2-配置文件(推荐)
- 方式 1 : 命令行配置
- 脚本 :
"dev" : "webpack-dev-server" - 运行到
npm run dev - 查看路径 : "http://localhost:8080/"
- 「wds」: Project is running at http://localhost:8080/
- 问题 1 : 自动打开?
- 解决 : 添加
--open - 问题 2 : 指定端口号
- 解决 : 添加
--port 3001 - 问题 3 : 热更新
- 解决 :
--hot( 整个页面和整个项目打包 )
- 方式 2 : 配置文件配置
// hot 不要写在配置文件里面,,不然的话还要配其他插件麻烦
"dev" : "webpack-dev-server --hot",
devServer : {
open : true,
port : 3001
}
第三阶段 项目打包上线的说明
- 开发模式 :
npm run dev==> 不会打包的 ,只会把项目放到服务器里 - 假设项目开发完成了,要上线,怎么操作?
2.1 执行 : `npm run build` 对项目进行打包,生成dist文件
2.2 模拟本地服务器 : 安装 : `npm i -g http-server`
2.3 把dist文件里的内容放到服务器里即可, 直接运行`http-server`
第四阶段
打包非js文件
webpack 只能处理 js 文件,非 js(css.less.图片.字体等)处理处理不了, 借助 loader 加载器
1 : 处理 css 文件
-
创建一个 css 文件, 然后在
main.js中引入import '../css/index.css';ul { style-list : none }
-
安装 :
npm i -D style-loader css-loader -
在
webpack.config.js中,添加个新的配置项module -
在
module中添加loader来处理css
// loader
module: {
rules: [
//1.处理 css
// 注意点 use执行loader 顺序 从右往左
// css-loader : 读取css文件内容,将其转化为一个模块
// style-loader :拿到模块, 创建一个style标签,插入页面中
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
2 : 处理 less 文件
- 创建一个 less 文件, 然后再 main.js 中 引入
import '../css/index.less'; - 安装 :
npm i -D less-loader less style-loader css-loader - 在 webpack.config.js 中, 配置
module->rules - 在 module 中, 添加 loader 来处理
less
ul {
background-color: aqua;
li {
&:hover {
color: yellow;
}
}
}
-
配置 :
{ test :/\.less$/, use : ['style-loader','css-loader','less-loader'] },
3 : 处理 图片 文件
设置背景图片.cls {
width: 300px;
height: 300px;
background: url('../css/4.jpg');
background-size: 100%;
}
-
安装 :
npm i -D url-loader file-loaderurl-loader (推荐) 和 file-loader 二选一即可
-
在 webpack.config.js 添加 loader 规则
// 处理图片
{ test : /\.(jpg|png)$/, use : ['url-loader'] },
- url-loader 默认会将图片转化为 base64 编码格式, 目的:提高性能
- file-loader 在车里图片时, 会对文件进行重命名 :
原始: background-image: url(../images/1.jpg);
处理后: background-image: url(9c9690b56876ea9b4d092c0baef31bb3.jpg);
- base64 编码格式的图片说明 :
- 精灵图 : 将一些小图片合并为一张图片,减少请求次数,提高性能
- 字体图标 :直接将一些小的图片,合并到字体文件中,并且不会失真
- base64 : 是一种编码格式,能够将图片、文字等常见的文件,转化为 base64 格式,这种字符串格式, 浏览器能够识别并且读取显示到页面中;
- base64 是一个字符串,也可以直接被内嵌到页面中, 或者 css 中
- 注意 : 大图片不适合用 base64 处理, 只有小的图标才适合 base64 处理
- 设置配置
方式1 :{ test : /\.(jpg|png)$/, use : ['url-loader?limit=57417'] },
方式2 :
{
test: /\.(jpg|png)$/, use: [
{
loader: 'url-loader',
options: {
// 比57417这个小 => 转化为base64
// 大于等于这个57417值 => 不会转base64 内部调用 file-loader 加载图片
limit: 57417
}
}
]
},
4: 处理 字体 文件
-
准备字体图标: 字体图标文件
iconfont或者 从阿里矢量图标里下载 -
拷贝到项目中的 css 文件夹中
-
在 main.js 中引入 css 文件
import '../css/iconfont/iconfont.css' -
使用 :
-
在 webpack.config.js 中配置
// 4. 处理字体图标
{ test:/\.(svg|woff|woff2|ttf|eot)$/,use:'url-loader'}
5:处理 ES6 语法
- 现在的项目都是使用 ES6 开发的, 但是这些新的 ES6 语法, 并不是所有的浏览器都支持, 所以就需要有一个工具,帮我们转成 es5 语法, 这个就是: babel
- babel
- Babel is a JavaScript compiler. ==> babel 是一个 JavaScript 编译器
- webpack 只能处理 import / export 这个 es6 模块化语法 而其他的 js 新语法,应该使用 babel 来处理
- 比如 :
var o = { ...obj }在谷歌上可以,edge 就不可以 - babel 的使用 :
- 6.1 安装 1 :
npm i -D babel-core babel-loader@7- babel-core 是 babel 的核心包
- babel-loader 加载 js 文件, 并将 js 代码内容交给 babel-core 解析为 es5 低版本的 js
- 6.2 安装 2 :
npm i -D babel-preset-env babel-preset-stage-2- babel-preset-* 表示能够解析什么版本的 js 语法
- babel-preset-env : 表示能够解析 es2015,es2016,es2017,es2018 这些标准的语法
- babel-preset-stage-2 : 用来解析经过会遇到,但是还没有被采纳为标准的语法
- 比如 : 'abc'.padStart(10, '6') : 准备 10 位字符,有 abc,前面不够用 6 补充,是 es2017 的语法,
babel-polyfill与babel-plugin-transform-runtime也是做兼容处理的,以前都是用这个,兼容更早的
- 6.3 配置 : 在 webpack.config.js 中添加一个 loader
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
- 6.4 在项目根目录中创建 babel 的配置文件,叫:
.babelrc
{
"presets": [
"env",
"stage-2"
],
-----------
// 暂时不用
// 如果未来某一天真的用到了polify
"plugins": [
["transform-runtime", {
"helpers": false,
"polyfill": true,
"regenerator": true,
"moduleName": "babel-runtime"
}]
- 6.5 测试 : 谷歌 和 edge
var obj = {
name: 'zs',
age: 20
}
var o = { ...obj }
console.log(o)
webpack中文链接
项目准备
一 : vue 是 单文件组件
直接注册组件的缺点
- 语法不会高亮
- 格式不好
- 没有专门写css样式的地方
单文件组件
- 什么是单文件组件 ?=> 后缀为 .vue 的文件
- 单文件组件的三个组成部分 (代码块 : scaffold 自动提示)
- template (模板结构)
- script 组件的代码逻辑
- style 样式
- 注意点 :
- 单文件组件,无法直接在浏览器中使用,必须经过 webpack 这种打包工具,处理后,才能在浏览器中使用
二 : 脚手架 介绍
脚手架 2.X ==> 2.Xvue
脚手架 3.X ==> 3.X vue
-
vue-cli 是 vue 的脚手架工具
-
作用 : vue-cli 提供了一条命令, 我们直接通过这条命令就可以快速的生成一个 vue 项目 (
vue init XX) 。 项目的基本结构、以及 webpack 配置项 全部配置 好了 -
为什么会有脚手架工具 ???
因为 webpack 配置繁琐, 阻止一批想用 vue 但是不会 webpack 的开发人员,所以作者直接将所有 vue 项目中用到的配置全部帮你写好了,这样,就不需要开发人员再去配置基础 webpack 配置项了,使用 vue-cli 这个脚手架工具后,再也不用担心 webpack 配置问题了, 我们前端只需要写 vue 代码, 来实现功能即可
三 : 脚手架工具使用
- 安装 :
npm i -g vue-cli - 初始化 vue 项目 :
vue init webpack 项目名称- 比如 :
vue init webpack vue-demo01
- 比如 :
- 项目安装过程 :
? Project name # 回车
? Project description # 回车
? Author # 回车
? Vue build standalone # => 运行时+编译 => 详见下面的问题1
? Install vue-router? # Yes
? Use ESLint to lint your code? # Yes => 详见下面的问题2
? Pick an ESLint preset Standard # standard
? Set up unit tests # No
? Setup e2e tests with Nightwatch? # No
? Should we run `npm install` for you after the project has been created? # (recommended) npm
- 如何开始
To get started:
cd vue-demo01
npm run dev
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",- --inline 意思是信息显示在哪里
- -progress 进度条
- 指定哪个文件作为 webpack 的配置文件 开发的
四 : 文件项目介绍
第一遍:文件夹, 第二遍再细化文件
# build - webpack 相关配置
- build.js - 生产环境构建代码
- check-version.js 检查 node、npm 等版本
- util.js 构建工具相关
- vue-loader.config.js vue-loader 的配置文件
- webpack.base.conf.js - webpack 的基础配置
- webpack.dev.conf.js - webpack 开发环境配置
- webpack.prod.conf.js - webpack 发布环境配置
# config - vue 基本配置文件(比如:监听端口(17), 使用 eslint:(26))
- dev.env.js - 开发环境变量
- index.js 项目的一些配置
- prod.env.js 生成环境变量
# node_modules - node 安装的依赖包
# src - 资源文件夹, 以后我们就在这个目录下写代码
- assets - 静态资源 (图片 css 都放在这)
- components - 公用组件
- router - 路由
- App.vue - 项目的根组件
- main.js - 项目的入口文件(总逻辑)
# static - 静态资源 (图片 json 数据之类的)
- .gitkeep git 保持文件,因为 git 上传,会忽略空文件夹
# .babelrc - babel 配置文件, (es6 语法编译配置,将 es6 转化为浏览器能够识别的代码)
# .editorconfig - 定义代码格式
- charset = utf-8 编码 utf8
- indent_style = space 缩进 空格
- indent_size = 2 缩进字符
- end_of_line = lf 回车作为换行的标记
- insert_final_newline = true 最近一空白行作为结尾
- trim_trailing_whitespace = true 清除最开始的空白
- 如果使用 ?
- 1. 安装 vscode 的 editorConfig for vscode
- 2. eslint 检测修复后
# .eslintignore - eslint 检测忽略的地方
- /build/
- /config/
- /dist/
- /\*.js 根目录下带.js 的
# .eslintrc.js - eslint 的配置文件
# .gitignore - git 的忽略文件
# .postcssrc.js - css 配置文件 (啥也没有处理)
# index.html - 页面入口
# package.json - 项目配置文件
4.1 参考 : standard
4.2 参考 : src
-
assets 静态资源
-
components 组件
-
App.vue 根组件 => 指定路由出口
- 脚手架之后,所有的组件都将渲染到 app.vue 中
-
app 中的 #app 还是 index.html 中的 #app, app.vue 中的会覆盖前者 可以通过分别添加 title 属性验证一下
-
路由出口要写在 app.vue 组件模板中
-
main.js
- 入口 js 文件
- 作用 : 创建 vue 实例,导入其他组件并挂在到 vue 实例上
Vue.config.productionTip = false不要打印提示- 检测 no new : 见后面的检测警告
new Vue({ el: '#app', // 目标显示 router, // 挂载路由 components: { App }, // 注册组件 App template: '<App/>' // 模板显示组件 app }) -
route/index.js => 路由
-
一定要记住 :
Vue.use(Router)模块化公工程中一定要安装路由插件 .js 就是一个模块 -
官网里 也提到
https://router.vuejs.org/zh/installation.html
五 : 问题处理
5.1 - 问题1 : 两种编译模式 和 @
参考 : vue.js => 安装 => 对不同构建本版本的解释
- 我们选择的是 Runtime + Compiler 模式 : ( 运行时 + 编辑器)
- 运行时模式 : 用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。
- 编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。
// 需要编译器
new Vue({
template: '<div>{{ hi }}</div>'
})
// 不需要编译器
new Vue({
render (h) {
return h('div', this.hi)
}
})
- 完整版版本选用 : ES Module (基于构建工具使用) : vue.esm.js
- build => webpack.base.config.js => 37 行 alias(别名) 'vue$': 'vue/dist/vue.esm.js',
- @ : 就是src的绝对路径
- build => webpack.base.config.js => 38 行 alias(别名) '@': resolve('src'),
router/index.js =>
import HelloWorld from '@/components/HelloWorld'
import HelloWorld from 'C:/users/.../src/components/HelloWorld'
5.2 - 问题2 : ESLint
-
概念 : ESLint 是在 JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。
- 在 vscode等编辑工具 中, 可以提示语法错误
- 在许多方面,它和 JSLint、JSHint 相似,除了少数的例外:
-
如何使用 eslint ?
- 1-安装vscode插件 ESlint
- 2-vscode设置里添加一些配置
"editor.formatOnSave": true, //#每次保存的时候自动格式化 "eslint.autoFixOnSave": true, // #每次保存的时候将代码按eslint格式进行修复 "eslint.validate": [ { "language": "html", "autoFix": true }, { "language": "vue", "autoFix": true }, { "language": "javascript", "autoFix": true }, { "language": "wpy", "autoFix": true } ], "prettier.eslintIntegration": true, // #让prettier使用eslint的代码格式进行校验 "javascript.format.insertSpaceBeforeFunctionParenthesis": true, "editor.formatOnType": true //#让函数(名)和后面的括号之间加个空格 -
关闭 Eslint :
- 参考 : config/index.js ==> 26行 :
dev.useEslint设置为false - 重启项目:
npm run dev 测试 : 删除 main.js 中的 /* eslint-disable no-new */ 关闭后 会报波浪线,但是不会报错了
- 参考 : config/index.js ==> 26行 :
5.3 问题3 : vscode安装 格式化插件 Prettier
- 安装 vscode 插件
Prettier - 功能1 : shift + alt + F => 格式化代码
- 功能2 : 配合 eslint : 见上一个问题的配置
5.4 问题4 : 检测警告
eslint-disable-next-line # 忽略检测下一行 可以使用单行注释/多行注释,其他都是多行注释
eslint-disable # 忽略当前整个文件
eslint-disable no-new # 忽略前面是new开头
六 : 项目演示
准备 :开启服务器+数据库
vue 项目学什么?
- 如何使用 vue 配合脚手架工具来完成一个项目
- 学习 ElementUI 组件库的使用
- 业务
- 3.1 登录和退出
- 3.2 用户+角色+授权+菜单
- 3.3 商品模块
七 : 开启本地服务器
画图 : 接口访问路径 : 前端页面 ===> 接口服务器 ===> 数据库服务器 ==> 数据库
第一小步 : 打开 : 数据库服务器
打开
phpStudy,点击启动
第二小步 : 导入数据库
-
打开
navicat -
点击
连接: 创建一个MySQL连接 -
用户名和密码 : root-root(不能乱写) 这个是和 config 里的配置一样的
-
创建一个数据库 :
shop_admin不要乱写, 选择倒数找到 unt-8 -
双击打开数据库
-
导入
sql语句=> 右键运行 SQL 文件 => shop-api 最后一个 sql 文件如果没有反应 : 表 => 右键刷新
第三小步 : 开启接口服务器
- 打开
shop-api - 运行
npm start - 显示成功 :
API 接口服务启动成功,占用端口 8888
第四小步 : 测试接口
http://localhost:8888/api/private/v1/login?username=admin&password=123456
项目使用接口 : (记得保存)
// shop-api里面有
第五小步 : 使用
- 每天做项目之前 :
-
- 每次都要先开启
phpStudy中的mySql
- 每次都要先开启
-
- 每次都要打开
shop-api, 运行npm start
- 每次都要打开
八 : ES6的模块语法 (基于webpack基类演示)
8.1 : export default 默认导出一个模块 ( 简单类型 + 复杂类型 )
-
导出 : export default
-
默认 只能导出一个
let str = 'abc' let num = 20; let obj = { name :'zs' } export default num // export default obj -
导入 : import
-
导入的名字可以任意
-
import res from './a.js' console.log(res)
8.2 export 导出多个模块, 都放在一个对象里
-
**导出 : export **
-
// 逻辑模块 // 登录一个函数 export let login = () => { console.log('登录'); } // 注册一个函数 export let register = () => { console.log('注册'); } -
导入 : import
-
// 方式1 import * as res from './a' console.log(res); res.login() res.register() // 方式2 import { login, register as reg } from './a' login() register()
8.3 import 模块
import axios from 'axios';
项目相关思路
一 : 初始化项目
vue init webpack shop_admin_35 清除不要的东西 (logo 和 hello world 组件 )
二 : 手动配置路由
安装 : npm i vue-router 创建一个router文件夹 / router.js 实例化路由 导出路由模块 , main.js 引入 ,挂载到vue实例上
/**
-
准备工作
-
- 安装 npm i vue-router
-
- 引入 import VueRouter from 'vue-router'
-
- 注意点 : vue-router 和 vuex 在模块化(把他们单独提到一个js文件里)工程中,需要使用 Vue.use(安装一下)
-
- 实例化 + 挂载 (导出去再挂载)
-
四个步骤
-
- 先把需要的组件创建出来 Login.vue Home.vue
-
- 走流程
-
- 入口 (url测试)
-
- 匹配规则
-
- 组件 (引入)
-
- 出口 */
三 : 创建两个子组件并且配置路由
1.先创建组件 /login/Login.vue 2.走流程
- 入口 : url手动写
- 规则 : {path :'/login', component: Login}
- 组件 : 引入即可
- 出口 : 在 app.vue 根组件 里 写一个出口
四 : Element-ui
链接 : element-cn.eleme.io/#/zh-CN/com… 概念 : element 是一个(基于 vue 2.X )组件库
安装: npm i element-ui ( -S == --save == 不写 ) (-D 开发阶段需要 发布阶段不需要)快速上手:引入(main.js)
// 引入 element
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// 安装一下 element-ui
Vue.use(ElementUI);
测试 : 找到官网里的组件,拿到一个 按钮 注意点 :template 里面只能由一个根元素 one root element
五 > Login 页面 表单
el-form el-form-item
六. 发送请求 登录
- 接口 (本地服务器, 接口文档 )
- 请求工具 : axios
安装 axios 引入 使用
七 : 跳转
声明式导航 ==> 组件 标签 编程式导航 this.$router.push('/home') ==> 事件里
八 :
router
route : 路由对象 , 包含url的信息对象, (path query, params) 解析 地址 #后面的信息
九 : 处理表单居中
注意1 : num='8' ==> 把字符串8 赋值给了num :num='8' ==> : 把 8 原来的类型赋值给num 注意2 : 如果想让label和input在一行 => 设置 label-width='100px' => el-form 如果想让 label 和 input 不在一行 =>不要设置 label-width