Vue2
前端 · 语雀 (yuque.com)
vue3: https://github.com/Panyue-genkiyo/vue3-learning
vue2依据脚手架:https://github.com/Panyue-genkiyo/vue-advance
vue基础不依赖脚手架:https://github.com/Panyue-genkiyo/vue-learning
Vue 是一套用于构建用户界面的 渐进式框框架
可自底向上逐层应用
渐进式就是逐步实现新特性的意思。如实现模块化开发 路由 状态管理的新特性 其特点是综合了Angular(模块化)和React(虚拟DOM)的优点
网络通讯:axios 前端通讯框架
页面跳转:vue-router
状态管理:vuex
Vue-UI:ICE
vue 特点
- 采用
组件化
模式,提高代码复用率,并且更好维护 声明式
编码 无需操作 DOM 提供开放效率
模板语法
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<div id="app">
<h1>{{ message }}</h1>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue! '
}
})
</script>
数据绑定
v-bind
单向绑定
<a v-bind:href="url">点击</a>
<a :href="url">点击</a>
v-model
双向绑定
Vue中有2种数据绑定的方式:
1.单向绑定(v-bind):数据只能从data流向页面。
2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
备注:
1.双向绑定一般都应用在表单类元素上(如:input、select等)
2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。
v-model:value 简写 v-model 因为 v-model 默认收集的就是 value 值
<input type="text" v-model="message">
<h1>{{ message }}</h1>
el
的两种写法
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue! ' + new Date().toLocaleString(),
url: 'http://www.baidu.com'
}
})
var app = new Vue({
data: {
message: 'Hello Vue! ' + new Date().toLocaleString(),
url: 'http://www.baidu.com'
}
})
// 挂载
app.$mount('#app')
data
的两种写法
对象式
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue! '
}
})
函数式
var app = new Vue({
el: '#app',
data() {
message: 'Hello Vue! '
}
})
生命周期
- 在 Vue 实例初始化时,Vue 将调用 $mount() 方法
- 在 Vue 实例挂载到 DOM 时,Vue 将调用 $mount() 方法
- 在 Vue 实例被销毁时,Vue 将调用 $destroy() 方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YFLmh6C2-1689212938289)(http://qiniu.xiaotao.cloud/生命周期.png)]
常用生命周期钩子
- mounted():发送 ajax 请求 启动定时器 初始化数据 创建子组件 绑定自定义事件 订阅信息等
- beforeDestroy():销毁子组件 取消订阅信息 移除自定义事件 移除定时器
销毁后借助vue开发着工具看不到任何信息·
销毁后自定义事件失效 但原生DOM事件仍然可以触发
js 调式
debugger;
MVVM (Model-View-ViewModel)
是一种软件架构设计模式 是一种简化用户界面的事件驱动编程方式
MVVM 源自于经典的MVC 模式
MVVM 的核心是 ViewModel 层 负责转化Model 中的数据对象来让数据更加容易管理和使用
M
: 模型(Model) 对应 data 中的数据
V
: 试图(View) 模块
VM
: 试图模型(ViewModel) Vue 实例对象
数据代理
回顾:
Object.defineProperty
let number = 18;
let person = {
name: '张三',
sex: '男'
}
Object.defineProperty(person, 'age',{
// value: 18,
enumerable:true, // 控制属性是否可以枚举, 默认为 false
writable: true, // 控制属性是否可以修改, 默认为 false
configurable: true, // 控制属性是否可以删除, 默认为 false
// 当有人读取 person 的 gae 时 get 就会被调用,并且返回值就是 age 的值
get (){
return number
},
// 当有人写入 person 的 age 时 set 就会被调用,并且传入的值就是 age 的值
set (value){
console.log(value);
}
})
数据代理:通过一个对象代理另一个对象中的属性的操作(读 写)
let obj1 = {x:100}
let obbj2 = {y:200}
Object.defineProperty(obj2, 'x',{
get (){
return obj1.x
},
set (value){
obj.x = value
}
})
vue 数据代理
vue2 vue3 响应式原理
vue2: Object.defineProperty
vue3: Proxy(代理)Reflect(反射)
let person = {
name: 'xiaotao',
age: 18
}
// 模拟vue2中实现响应式
let p = {}
Object.defineProperty(p, 'name', {
get() { // 有人读取name时调用
return person.name
},
set(newValue) { // 有人修改name时调用
console.log('有人修改name属性 我要去更新页面');
person.name = newValue
}
})
Object.defineProperty(p, 'age', {
get() { // 有人读取age时调用
return person.age
},
set(newValue) { // 有人修改age时调用
console.log('有人修改age属性 我要去更新页面');
person.age = newValue
}
})
// 模拟vue3中实现响应式
/* - 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射): 对源对象的属性进行操作。 */
let proxy = new Proxy(person, {
get(target, key) {
console.log(`有人读取 ${key} 属性`);
return Reflect.get(target, key)
},
set(target, key, value) {
console.log(`有人修改或添加 ${key} 属性 我要去更新页面`);
return Reflect.set(target, key, value)
},
deleteProperty(target, key) {
console.log(`有人删除 ${key} 属性 我要去更新页面`);
return Reflect.deleteProperty(target, key)
}
})
mixin 混入
作用: 将一段需要重复 编写的方法 抽离出来 定义下成为一个全局可以使用的组件
方便代码复用
mixin.js
export const mixin = {
methods: {
showName() {
alert(this.name);
}
}
}
全局混入
main.js
// 全局引入混合
import {mixin} from './mixin'
Vue.mixin(mixin)
new Vue({
render: h => h(App),
}).$mount('#app')
局部混入
<template>
<div>
<h3 @click="showName">学生姓名:{{name}}</h3>
</div>
</template>
<script>
import { mixin } from '../mixin'
export default {
name: 'Student',
data() {
return {
name: '张三'
}
},
mixins: [mixin]
}
</script>
vue 插件 plugins
增强 vue 的使用
plugins.js
export default {
install(Vue, x, y, z) {
console.log(x, y, z);
console.log('install', Vue);
// 全局过滤器
Vue.filter('mySlice', function(value) {return value.slice(0, 4)});
// 全局指令
Vue.directive('fbind', {
// 指令与元素成功绑定时
bind(el, binding, vnode) {
console.log('bind');
el.value = binding.value;
},
// 指令所在元素被插入页面时
inserted(el, binding, vnode) {
console.log('inserted');
el.focus();
},
// 指令所在模板被重新解析时
update(el, binding, vnode) {
console.log('update');
el.value = binding.value;
}
});
// 全局混合
Vue.mixin({
data() {return {a: 1, b: 2}},
});
// 给 Vue 原型添加一个方法
Vue.prototype.hello = function() {
console.log('hello');
}
}
}
main.js
// 引入自定义插件
import plugins from './plugins'
// 使用插件
Vue.use(plugins)
浏览器本地存储
localStorage
localStorage.setItem('data', JSON.stringify(data));
let data = localStorage.getItem('data');
localStorage.removeItem('msg');
localStorage.clear();
sessionStorage
sessionStorage.setItem('data', JSON.stringify(data));
let data = sessionStorage.getItem('data');
sessionStorage.removeItem('msg');
sessionStorage.clear();
Axios
是一个开源的可在浏览器端 和 NodeJS 的异步通信框架 主要作用就是实现AJAX 的异步通信
功能:
- 从浏览器创建 XMLHttpRequests
- 从node.js 创建 http 请求
- 支持 Promise API [js 链式编程]
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转化 JSON 数据
- 客户端支持防御 XSPF (跨站请求伪造)
为什么使用?
由于 Vue.js 是一个视图层框架 并且严格遵守 SoC(关注度分离原则) 所以 Vue 不包含 AJAX 的通信功能 为了解决通信问题
作者单独开发 vue-resource , 2.0 后停止更新 推荐使用 Axios 框架
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com",
"page": 1,
"isNonProfit": true,
"address": {
"street": "含光门",
"city": "陕西西安",
"country": "中国"
},
"links": [
{
"name": "bilibili",
"url": "https://space.bilibili.com/95256449"
},
{
"name": "狂神说Java",
"url": "https://blog.kuangstudy.com"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<div>{{info.name}}</div>
<div>{{info.address.country}}</div>
<a v-bind:href="info.url">点我</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data() {
return{
info: {
name: null,
address: {
country: null,
city: null,
street: null
},
url: null
}
}
},
mounted() { //钩子函数 链式编程
axios
.get('data.json')
.then(response => (this.info = response.data));
}
})
</script>
</body>
</html>
计算属性
1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
let vm = new Vue({
el: '#app',
data: {
fastName: 'xiao',
lastName: 'tao'
},
computed: {
fullName() {
return this.fastName + ' - ' + this.lastName
}
}
})
第一个 Vue-cli 项目
vue-cli 是官方提供的一个脚手架 用于快速生成一个vue 的项目模板
主要的功能:
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
需要的环境:
-
Node.js 官网下载
-
Git
-
镜像 https://npm.taobao.org/mirrors/git-for-windows/
确认nodejs 是否安装成功
cmd
node -v # 查看是否能打印出版本号即可
npm -v # 查看是否能打印出版本号即可
npm : 就是一个软件包管理工具 就和linux 下的apt 软件安装差不多
安装 Node.js 淘宝镜像加速器 (cnpm)
npm install cnpm -g # -g 就是全局安装
# 或使用如下
npm install --registry=https://registry.npm.taobao.org
安装 vue-cli
cnpm install vue-cli -g
测试是否安装成功
查看可以基于哪些模板创建vue 通常选择 webpack
vue list
创建第一个 vue-cli
cmd 去到一个文件夹 如 D:vue
vue init webpack myvue # myvue 项目名
? Project name myvue
? Project description A Vue.js project
? Author xiaotao
? Vue build standalone
? Install vue-router? No
? Use ESLint to lint your code? No
? Set up unit tests Noc
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) no
vue-cli · Generated "myvue".
# Project initialization finished!
# ========================
To get started:
cd myvue
npm install (or if using yarn: yarn)
npm run dev
Documentation can be found at https://vuejs-templates.github.io/webpack
初始化并运行:
cd myvue
npm install
npm run dev
打包成功访问
脚手架创建vue项目
vue create 项目名称
WebPack
安装打包工具
npm install webpack -g
npm install webpack-cli -g
# 删除
npm uninstall -g webpack
npm uninstall -g webpack-cli
测试安装成功
webpack -v
webpack-cli -v
配置
创建webpack.config.js
配置文件
-
entry :入口文件 指定webpack用哪个文件作为项目的入口
-
output:输出 指定webpack把处理完成的文件放置到指定路径
-
module:模块 用于处理各种类型的文件
-
resolve:设置路径指向
-
piugins:插件 如:热更新 代码重用
-
watch:监听 用于设置文件改动后直接打包
-
创建一个项目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iiJTQxcZ-1689212938290)(Vue.assets/2021-10-06_163955.png)]
-
创建一个名为modules 的目录 用于放置js模块
-
在modules下创建模块文件 如 hello.js 用于编写js模块相关代码
// 暴露一方法 exports.sayHei = function(){ document.write('<h1>ES6 规范</h1>') };
-
在modules下创建一个main.js 的入口文件 用于打包设置entry属性
var hello = require('./hello'); // 类似于java 的导包 导入 hello.js hello.sayHei();
-
在项目目录下创建webpack.config.js 配置文件
module.exports = {
entry: './modules/main.js', // 入口
output: {
filename: './js/bundle.js' //输出到哪
}
}
-
使用 webpack 命令打包
webpack webpack --watch # 类似于 idea 的热部署 实时更新
打包成功后:
-
在项目目录下创建index.html 引入打包后的js
<script src="dist/js/bundle.js"></script>
运行
传值
父传子
使用 props
获取父传过来的值
父
data() {
return {
todo: [
{
id: '001',
name: '吃饭',
status: true
}
]
}
}
<template>
<user-list :todo='todo' />
</template>
子
<div>
{{ todo }}
</div>
props: ['todo']
子传父
先在父类定义一个接收数据的方法 子类调用该方法传值给父类
父
<user-header :receive='receive' />
methods: {
receive(x) {
console.log('收到子组件的值',x)
}
}
子
props: ['receive'],
// 子组件传值给父组件
this.receive(todo)
父传孙
父
data() {
return {
todo: [
{
id: '001',
name: '吃饭',
status: true
}
]
}
}
<template>
<user-list :todo='todo' />
</template>
子
<template>
<user-itme :todo='todo' />
</template>
props: ['todo']
孙
props: ['todo']
<div>
{{ todo }}
</div>
自定义事件实现 子传父
方式一
父
<student @xiaotao="getStudentName" />
getStudentName(name) {
console.log('学生姓名: '+ name);
}
子
<button @click="sendStudentName">把学生姓名给App</button>
sendStudentName() {
// $emit 触发自定义事件
// 触发 Student 组件实例上的 xiaotao 事件
this.$emit('xiaotao', this.name);
}
方式二
父
<student ref='student'/>
mounted() {
this.$refs.student.$on('xiaotao', this.getStudentName);
}
解绑事件
<button @click="unbind">解绑 xiaotao 事件</button>
unbind() {
// $off 解绑自定义事件
// 解绑 Student 组件实例上的 xiaotao 事件
this.$off('xiaotao');
// 解绑多个事件
// this.$off(['xiaotao', 'xiaotao2']);
// 解绑所有
this.$();
}
给实例绑定原生事件
<student @click.native="show" />
show() {
console.log('点击了');
}
全局事件总线
任意组件间通信
配置全局事件总线
new Vue({
beforeCreate() {
Vue.prototype.$bus = this // 配置全局事件总线
}
})
取值组件
mounted() {
this.$bus.$on('xiaotao', (name) => {
console.log(name);
});
},
// 销毁自定义事件
beforeDestroy() {
this.$bus.$off('xiaotao');
}
传值组件
sendStudentName() {
this.$bus.$emit('xiaotao', this.name);
}
信息订阅与发布
任意组件间通信
- 订阅消息: 消息名
- 发布消息: 消息内容
使用第三方库
pubsub.js
安装
npm i pubsub.js
引入
import PubSub from 'pubsub-js'
取数据
mounted() {
this.pudid = PubSub.subscribe('xiaotao', (msg, data) => {
console.log('有人发布了 '+ msg +' 消息, 消息回调执行了: '+data);
});
},
beforeDestroy() {
PubSub.unsubscribe(this.pudid);
}
提供数据
PubSub.publish('xiaotao', this.name);
$nextTick()
DOM 节点更新后执行
过渡与动画
Animate.css
Animate.css | A cross-browser library of CSS animations.
安装
npm install animate.css --save
导入
import 'animate.css';
<transition-group
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
appear>
<h1 v-show="!isShow" key="1">hello</h1>
<h1 v-show="isShow" key="2">vue</h1>
</transition-group>
vue axios
npm i axios
<template>
<div>
<button @click="getDept">获取部门信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "App",
methods: {
getDept() {
axios.get('http://localhost:5000/').then(res => {
console.log(res.data)
}, err => {
console.log(err)
})
}
},
};
</script>
存在问题 跨域
解决方式:
后端: cors nainx
前端:
代理服务器 vue-cli
配置 vue.config.js
devServer: {
port: 8080,
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
pathRewrite: {
'^/api': '' // 去掉路径中的api
}
}
}
}
修改请求
axios.get('http://localhost:8080/api')
vue-resource
跟 axios 类似 是 vue 的一个插件库
npm i vue-resource
main.js
import vueResource from 'vue-resource'
Vue.use(vueResource)
插槽 slot
默认插槽
<template>
<div class="container">
<Category title="美食">
<img src="https://avatars.githubusercontent.com/u/89018496?v=4" alt="" srcset="">
</Category>
<Category title="游戏">
<ul>
<li v-for="item in games" :key="item.id">{{item.name}}</li>
</ul>
</Category>
<Category title="电影"></Category>
</div>
</template>
<div class="category">
<h3>{{title}}分类</h3>
<slot>默认值如果组件不传值就会显示</slot>
</div>
具名插槽
<template>
<div class="container">
<Category title="美食">
<img slot="center" src="https://avatars.githubusercontent.com/u/89018496?v=4" alt="" srcset="">
<a slot="footer" href="https://xiaotao.cloud">更多美食</a>
</Category>
<Category title="游戏">
<ul slot="center">
<li v-for="item in games" :key="item.id">{{item.name}}</li>
</ul>
<template v-slot:footer>
<a slot="footer" href="https://xiaotao.cloud">更多游戏</a>
<h4>欢迎使用!</h4>
</template>
</Category>
<Category title="电影">
</Category>
</div>
</template>
<div class="category">
<h3>{{title}}分类</h3>
<slot name="center">默认值如果组件不传值就会显示</slot>
<slot name="footer">默认值如果组件不传值就会显示2</slot>
</div>
作用域插槽
数据在子类 都是要在父类遍历
<template>
<div class="container">
<Category title="游戏">
<template scope="{games}">
<ul>
<li v-for="item in games" :key="item.id">{{ item.name }}</li>
</ul>
</template>
</Category>
<Category title="游戏">
<template scope="{games}">
<ol>
<li v-for="item in games" :key="item.id">{{ item.name }}</li>
</ol>
</template>
</Category>
<Category title="游戏">
<template scope="{games}">
<h4 v-for="item in games" :key="item.id">{{ item.name }}</h4>
</template>
</Category>
</div>
</template>
<template>
<div class="category">
<h3>{{ title }}分类</h3>
<slot :games="games">默认值如果组件不传值就会显示</slot>
</div>
</template>
vuex
解决多组件共享数据问题
全局事件总线
vuex
使用
- 多个组件依赖同一状态
- 来自不同组件的行为需要变更同一状态
安装
vue2 要使用 vuex@3
vue3 要使用 vuex@4
npm i vuex
// 该文件用于创建 vuex 最核心的 store,并将其绑定到 Vue 实例上。
import Vue from 'vue'
// 引入 Vuex
import Vuex from 'vuex'
// 使用
Vue.use(Vuex)
// 用于响应组件中的动作
const actions = {}
// 用于操作数据
const mutations = {}
//用于储存数据
const state = {}
// 创建 并 暴露 store 实例
export default new Vuex.Store({
state,
actions,
mutations
})
main.js
import store from './store'
new Vue({
render: h => h(App),
store,
beforeCreate() {
Vue.prototype.$bus = this // 配置全局事件总线
}
}).$mount('#app')
然后就有:
路由
vue-router
是 vue的一个插件库, 专门用来实现 SPA 应用的
SPA 应用
- 单页面 WEB 应用(Single page web application)
- 整个应用只有
一个完整的页面
- 点击页面中的导航链接, 只会
局部刷新
- 数据需要通过 ajax 请求
路由
- 一个路由就是一组映射关系(key-value)
key
为路径value
可能是 function 或 component
安装
npm i vue-router@3 # vue2
npm i vue-router@4 # vue3
import VueRouter from 'vue-router'
Vue.use(VueRouter)
具体看 github 提供的代码有详细讲解