vue官网

<!-- #region 
app不能挂载到 html body
el 元素挂载的位置
data 模型数据 可以多个
{{}}插值表达是式 有运算功能 拼接功能

vue的执行原理 还是底层的dom操作 vue的代码会被框架变成原生JS

模板V +VM框架桥梁+ 数据M + VM:vue ===>MVVM
-->
<!DOCTYPE html>
<html lang="en">
<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">
<title>Document</title>
<script src="js/vue.js"></script>
<style>
/* 解决屏闪 */
[v-cloak]{
display:none;
}
</style>
</head>
<body >
<!-- #region
app不能挂载到 html body
el 元素挂载的位置
data 模型数据 可以多个
{{}}插值表达是式 有运算功能 拼接功能

vue的执行原理 还是底层的dom操作 vue的代码会被框架变成原生JS

模板M +VM框架桥梁+ 数据V + VM:vue ===>MVVM
-->
<div id="app">
<h3 id="msg">传统方式</h3>
<h1 v-cloak>{{msg}}</h1>
<h2 v-cloak>{{exam}}</h2>
<h2 v-cloak>{{'年龄:'+exam}}</h2>
<h1 v-bind:title="message">鼠标悬停几秒钟查看此处动态绑定的提示信息!</h1>
</div>
</body>
<script>
//document.querySelector("#msg").innerHTML="改变属性";

var app=new Vue({
//ID
el:'#app',
//属性
data: {
msg: 'Vue修改',
exam:19+1,
message:'页面加载于'+new Date().toLocaleString()
}
});
</script>
</html>

解决屏闪

 /* 解决屏闪 */
样式
[v-cloak]{
display:none;
}

界面
<h2 v-cloak>{{exam}}</h2>

JS
var app=new Vue({
//ID
el:'#app',
//属性
data: {
msg: 'Vue修改',
exam:19+1,
message:'页面加载于'+new Date().toLocaleString()
}
});

常用指令

v-cloak 解决闪屏

样式

[v-cloak]{
display:none;
}

界面

<h1 v-cloak>{{msg}}</h1>

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
msg: 'Vue修改',
exam:19+1,
message:'页面加载于'+new Date().toLocaleString()
}
});

v-text 显示数据

理解

v-text="变量名" 显示数据

界面

<h1 v-text="msg">不哈</h1>

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
message:'页面加载于'+new Date().toLocaleString(),
msg:'你好temo'
}
});

v-html显示识别html

理解

v-html="变量名" 可以显示html

界面

<h2 v-html="jk"></h2>

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
jk:'<i>nh1<i/>'
}
});

v-pre 不编译

理解

v-pre  不编译 赋值不了 不需要表达式

界面

<h2 v-pre>{{msg}}</h2>

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
msg:'你好temo',

}
});

v-once 只渲染一次

理解

v-once 只能渲染一次 在浏览器改无效

界面

<h3 v-once>乌克兰{{wkl}}</h3>

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
wkl:'admi123456',

}
});

v-show 隐藏显示

理解

v-show="条件"  切换元素的display 隐藏显示

界面

<h1 v-show="1==1">v-show</h1>

v-model 数据双向绑定

理解

v-model="变量名" 数据双向绑定
<!--
//双向数据绑定 互相影响 给变文本框数据也会改变
从界面到指令
从指定到界面
-->

界面

<input type="text"  v-model="info" />

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
info:'文本框'
}
});

v-if 判断

理解

条件判断 
v-if="条件" 判断是否满足 满足显示一块 不满组不显示

界面

<div v-if="1==0">大家好</div>

v-else 否则

理解

v-if="条件" v-else   if判断 满足if里面的 不满足就else里面的

界面

<div>
<div v-if="Math.random()>0.5">
ture
</div>
<div v-else>false</div>
</div>

v-else-if

理解

v-else-if="条件" 条件判断 

界面

<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else-if="type === 'C'">
C
</div>
<div v-else>
Not A/B/C
</div>

v-for循环

理解

for 循环

v-for="自定义循环名字 in 循环的变量名" for循环 显示数据{{自定义名字}}

v-for 的默认行为会尝试原地修改元素而不是移动它们。要强制其重新排序元素,你需要用特殊 attribute key 来提供一个排序提示:

<div v-for="item in items" :key="item.id">
{{ item.text }}
</div>

界面

<ul>
<li v-for="item in items">{{item}}</li>
</ul>

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
items:[1,2,3,6,7]
}
});

v-on绑定事件监听器

理解

v-on:事件="变量"  绑定事件监听器  v-on:click=""  v-on:mouseover="" ...

缩写:@
.stop - 调用 event.stopPropagation()。
.prevent - 调用 event.preventDefault()。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。

界面

<button v-on:click="dj">v-on:click</button>
<button v-on:mouseover="yr">v-on:mouseover</button>

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
},
methods:{
dj:()=>{
alert('v-on:click');
},
yr:()=>{
alert('v-on:mouseover');
}
}
});

v-bind 动态绑定属性

理解

v-bind:属性="变量"   动态地绑定一个或多个 attribute 属性  v-bind:src || v-bind:width || v-bind:height...

缩写: :

界面

<img v-bind:src="vimg" v-bind:width="vimgw" v-bind:height="vimgh"> 

js

var app=new Vue({
//ID
el:'#app',
//属性
data: {
vimg:'./img/3210.webp',
vimgw:'700px',
vimgh:'500px'
}
});

示例

<!-- 绑定一个 attribute -->
<img v-bind:src="imageSrc">

<!-- 动态 attribute 名 (2.6.0+) -->
<button v-bind:[key]="value"></button>

<!-- 缩写 -->
<img :src="imageSrc">

<!-- 动态 attribute 名缩写 (2.6.0+) -->
<button :[key]="value"></button>

<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName">

<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]">

<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>

<!-- 绑定一个全是 attribute 的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>

<!-- 通过 prop 修饰符绑定 DOM attribute -->
<div v-bind:text-content.prop="text"></div>

<!-- prop 绑定。“prop”必须在 my-component 中声明。-->
<my-component :prop="someThing"></my-component>

<!-- 通过 $props 将父组件的 props 一起传给子组件 -->
<child-component v-bind="$props"></child-component>

<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>

事件处理

带参数也有默认的event 不带参数也有

点击调方法

<button v-on:click="sj">事件1</button>

var app=new Vue({
el:'#app',
data: {
num:1
},
//方法
methods: {
sj:function(){
this.num++;
console.log(this);
}
}
})

方法带括号

<button v-on:click="sj()">事件1</button>

方法带参数

<button @click="cc(3,6)">传参数</button>

var app=new Vue({
el:'#app',
data: {
num:1
},
//方法
methods: {
cc:function(a,b){
this.num=a+b;
}
}
})

data变量参数

<button @click="cc2(num,6)">传参数</button>

var app=new Vue({
el:'#app',
data: {
num:1
},
//方法
methods: {
cc2:function(a,b){
this.num=a+b;
//传了值也有event
console.log(event.target);
}
}
})

点击直接运行不调方法

<button v-on:click="num--">事件2</button>

多个按钮输出当前按的按钮

<button @click="hello">A</button>
<button @click="hello">B</button>
<button @click="hello">C</button>

var app=new Vue({
el:'#app',
data: {
num:1
},
//方法
methods: {
hello:function(event){
//如果是hello没有括号的时候默认是有一个参数 event参数
//event.target得到事件源
alert("我是"+event.target.innerHTML);
}
}
})

事件修饰符

冒泡

在事件后加.stop

<div @click="show('父')">
父盒子
<div @click.stop="show('子')">
子盒子
</div>
</div>

//不阻止的话就会输出 父 子
var app=new Vue({
el:"#app",
data: {
num:0
},
methods: {
show: function(info) {
console.log(info);
}
}
});

只触发一次

事件后加.once

 <!--只会触发一次 点了一次就没用了-->
<a v-on:click.once="doThis">ayd1</a>



var app=new Vue({
el:"#app",
data: {
num:0
},
methods: {
doThis:function(){
alert("点击了!");
}
}
});

阻止事件的默认行为

事件后加.prevent

h1>练习2</h1>
<a href="http://www.baidu.com" @click.prevent="lj">百度</a>



var app=new Vue({
el:"#app",
data: {
num:0
},
methods: {
lj:function(){
//阻止默认行为
//event.preventDefault();
alert("点击了链接!");
}
}
});

通过冒泡判断点的按钮

<h1>练习</h1>
<div @click="dj">
<button>A</button>
<button>B</button>
<button>C</button>
</div>

var app=new Vue({
el:"#app",
data: {
num:0
},
methods: {
dj:function(event) {
console.log(event.target.innerHTML);
}
}
});

按键修饰符

  • .enter
  • .tab
  • .delete (捕获“删除”和“退格”键)
  • .esc
  • .space
  • .up
  • .down
  • .left
  • .right

回车提交

<form>
Name:<input type="text" v-model="name" />
Password:<input type="password" id="pa" v-model="pass" @keyup.enter="login"/>
<input type="button" value="登入">
</form>



var app=new Vue({
el:'#app',
data: {
name:"",
pass:""
},
methods:{
//双向绑定
login:function(){
console.log(this.name);
console.log(this.pass);
this.name='',
this.pass=''
}
}
});

del删除密码

<h1>按del</h1>
<form>
Name:<input type="text" v-model="name" />
Password:<input type="password" v-model="pass" @keyup.delete="de"/>
<input type="button" value="登入">
</form>

var app=new Vue({
el:'#app',
data: {
name:"",
pass:""
},
methods:{
//按删除 就删除密码
de:function(){
this.pass=''
}
}
});

自定义按键名字

<h1>自定义</h1>
<input type="text" @keyup.da="r"/>

//自定义名字
Vue.config.keyCodes.da=82;

var app=new Vue({
el:'#app',
data: {
name:"",
pass:""
},
methods:{
//自定义r键
r:function(){
console.log("按了R");
}
}
});

属性绑定

href绑定

<a v-bind:href="href">去百度2</a>
<a :href="href">去百度2</a>

var app=new Vue({
el:"#app",
data: {
href:"http://www.baidu.com",
}
});

图片循环切换

<img :src="arr[index]" width="500px" height="300px"><button @click="huan2()"></button>

var app=new Vue({
el:"#app",
data: {
href:"http://www.baidu.com",
info:"今天送皮肤",
imgurl:'img/11.webp',
id:0,
arr:['img/11.webp','img/12.webp','img/22.webp','img/3210.webp'],
index:0,
h1ar:''
},
methods:{
huan2:function(){
this.index++;
if(this.index==this.arr.length){
this.index=0;
}
}
}
});

不使用v-model实现双向绑定

<h1>{{h1ar}}</h1>
<input type="text" :value="h1ar" @keyup="gb"/>

var app=new Vue({
el:"#app",
data: {

},
methods:{
gb:function(){
this.h1ar=event.target.value;
}
}
});

样式绑定

对象语法绑定class

样式

.atice{
border:10px solid pink;
width: 100px;
height: 100px;
}
.err{
background-color:aquamarine
}

html

<!-- 定义:atice是样式的名称:随便名字  
随便名字可以在下面进行true(使用这个样式) || false(不使用这个样式) -->
<h1>对象语法绑定</h1>
<div :class="{atice:isActive,err:iserr}">
用对象
</div>

js

var app = new Vue({
el: "#app",
data: {
val: "",
isActive:false,
iserr: true
}
});

数组语法绑定class

样式

.atice{
border:10px solid pink;
width: 100px;
height: 100px;
}
.err{
background-color:aquamarine
}

html

<!-- 定义[自定义名字1,自定义名字2] data里面 自定义名字1="样式表名" -->
<h1>数组语法绑定</h1>
<div :class="[c1,c2]">
用数组
</div>

js

var app = new Vue({
el: "#app",
data: {
c1:'atice'
c2:'err',
}
});

对象数组混合

<h1>混合使用</h1>
<div :class="[c3,{err:herr}]">
混合使用
</div>

var app = new Vue({
el: "#app",
data: {
c3:'atice',
herr:true
},
methods: {
nj: function () {
this.val = event.target.value;
},
//切换样式表
dxhys:function () {
this.isActive = !this.isActive;
this.iserr=!this.iserr;
},
szhys:function(){
this.c2='';
}
},
});

分支

<div id="app">
<!--v-if 返回true false 控制标签的隐藏显示-->
<div v-if="score>=90">优秀</div>
<div v-else-if="score>=80">中等</div>
<div v-else-if="score>=70">一般般</div>
<div v-else>补课</div>

<hr>
<!--v-show 标签隐藏显示-->
<div v-show="flag">目标</div>
</div>

var app = new Vue({
el: "#app",
data: {
score:76,
flag:false
},
methods: {

},
});

循环

循环数组

界面

<h1>循环数组</h1>
<!--可以不用括号-->
<ul>
<li v-for="(fls,i) in fruit" :class="i%2!=0?'ls':''">{{i}} {{fls}}</li>
<li v-for="fls,i in fruit" :class="{ls:i%2!=0,ls2:i%2==0}">{{i}} {{fls}}</li>
</ul>

js

var app = new Vue({
el: "#app",
data: {
fruit: ["apple", "orange", "banana", "watermelon", "almond"],
},
methods: {},
});

image

循环对象 数组里有对象

界面

<h1>循环对象 数组里面有对象</h1>
<table border="1px" cellspacing="0px" width="500px" height="200px" style="text-align:center">
<tr>
<td>ID</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<!--:key="st.id" 循环对象的时候确保唯一性 key是唯一的 -->
<tr v-for="st in mystud" :key="st.id">
<td>{{st.id}}</td>
<td>{{st.name}}</td>
<td>{{st.age}}</td>
</tr>
</table>

js

var app = new Vue({
el: "#app",
data: {
mystud: [
{ id: 1, name: "st1", age: 18 },
{ id: 2, name: "st2", age: 20 },
{ id: 3, name: "st3", age: 19 },
]
},
methods: {},
});

image

循环对象

<h1>循环对象</h1>
<table border="1px" cellspacing="0px" width="500px" height="200px" style="text-align:center">
<tr>
<td>下标</td>
<td>名称</td>
<td></td>
</tr>
<!--ms得到的是对象的值 name是对象的名称 index是下标 名字自定义 可以不用加括号-->
<tr v-for="(ms,name,index) in mys" >
<td>{{index}}</td>
<td>{{name}}</td>
<td>{{ms}}</td>
</tr>
</table>

js

var app = new Vue({
el: "#app",
data: {
mys:{
id:101,name: "st101", age:90
},
},
methods: {},
});

image

样式绑定 循环 选项卡切换图片示例

<!DOCTYPE html>
<html lang="en">
<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" />
<title>Document</title>
<script src="js/vue.js"></script>
<style>
ul li {
float: left;
width: 100px;
list-style: none;
height: 50px;
border: 1px solid pink;
text-align: center;
line-height: 50px;
}
.bg {
background-color: pink;
}
#d2{
margin-top: 30px;
margin-left: 30px;
}
</style>
</head>
<body>
<div id="app">
<ul>
<li @click="cf(fl.id)" :class="currid==fl.id ?'bg':''" v-for="fl in flis">{{fl.name}}</li>
</ul>
<br>
<div id="d2">.
<img v-if="currid==f.id" v-for="f in flis" :src="f.imgurl" width="500px" height="300px"/>
</div>
</div>
</body>
<script>
var app = new Vue({
el: "#app",
data: {
flis: [
{ id: 1, name: "选项卡1", imgurl: "img/11.webp" },
{ id: 2, name: "选项卡2", imgurl: "img/12.webp" },
{ id: 3, name: "选项卡3", imgurl: "img/22.webp" },
],
currid:1
},
methods: {
cf: function (a) {
this.currid = a;
},
},
});
</script>
</html>

image

表单

文本框

界面

<form action="">
<span>姓名:</span><span><input type="text" v-model="una"/></span>
<button type="submit" @click.prevent="tj">提交</button>
</form>

js

var app = new Vue({
el: "#app",
data: {
una:''
},
methods: {
//登录方法
tj:function () {
console.log("姓名:"+this.una);
}
},
});

下拉框

界面

<!--multiple支持多选 接收的变量要换成数组-->
<select v-model="city">
<option disabled>请选择城市</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="南京">南京</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
</select>

js

var app = new Vue({
el: "#app",
data: {
city:'请选择城市'
},
methods: {
//登录方法
tj:function () {
console.log("下拉框:"+this.city);
}
},
});

单选框

界面

<input type="radio"  value="男" v-model="sex"/>
<input type="radio" value="女" v-model="sex"/>

js

var app = new Vue({
el: "#app",
data: {
sex:'男',
ches:['打球'],
city:'请选择城市',
ms:''
},
methods: {
//登录方法
tj:function () {
console.log("性别:"+this.sex);
}
},
});

多选框

界面

<input type="checkbox" value="打球" v-model="ches"/>打球
<input type="checkbox" value="打游戏" v-model="ches"/>打游戏
<input type="checkbox" value="游泳" v-model="ches"/>游泳

js

var app = new Vue({
el: "#app",
data: {
ches:['打球']
},
methods: {
//登录方法
tj:function () {
console.log("复选框:"+this.ches);
}
},
});

文本域

界面

<textarea v-model="ms" rows="5" cols="50"placeholder="请输入描述">

</textarea>

js

var app = new Vue({
el: "#app",
data: {
ms:''
},
methods: {
//登录方法
tj:function () {
console.log("描述:"+this.ms);
}
},
});

表单修饰符

number 数值

年龄:<input type="text" v-model.number="age" v-focus /><br />

trim 去空格

地址:<input type="text" v-model.trim="address" v-color="bgc" /><br />

lazy 失去焦点改变

姓名:<input type="text" v-model.lazy="stname" v-colorg="bg2" />

自定义指令

生命周期:bind inserted update componentUpdate unbid

bind:初始化

inserted:挂载完毕

update数据更新后

componentUpdate 元素更新后

unbind卸载

三个参数 el,binding vnode

// ele指指令所在的元素

​ // binding包含指令的详细信息 ,比如读取参数

​ // vnode是虚拟节点对象,比如获取组件对象

自定义全局指令 不带参数

定义在new Vue的前面

//自定义的指令 使用v-名字  focus是自定义名字
Vue.directive("focus", {
//el是绑定的元素
inserted: function (el) {
el.focus(); //js
},
});

使用 v-focus v-上面自定义的名字

年龄:<input type="text" v-model.number="age" v-focus /><br />

自定义全局指令 带参数

多个值

  //带参数自定义指令
Vue.directive("color", {
//bindc是自定义 bindc.value.color 就是
//bindc的值下面的color属性 JSON对象
bind: function (el, bindc) {
el.style.backgroundColor = bindc.value.bgcolor;
el.style.color = bindc.value.color;
},
});

var app = new Vue({
el: "#app",
data: {
bgc: {
color: "red",
bgcolor: "pink",
}
},
methods: {
dl: function () {
console.log("年龄:" + this.age + 10);
console.log("地址:" + this.address);
console.log("姓名:" + this.stname);
},
}
});

使用

地址:<input type="text" v-model.trim="address" v-color="bgc" /><br />

单个值

//带参数自定义指令
Vue.directive("colorg", {
//bindc是自定义
bind: function (el, bindcj) {
el.style.backgroundColor = bindcj.value;
},
});

var app = new Vue({
el: "#app",
data: {
bg2: "pink",
},
methods: {
dl: function () {
console.log("年龄:" + this.age + 10);
console.log("地址:" + this.address);
console.log("姓名:" + this.stname);
},
}
});

使用

姓名:<input type="text" v-model.lazy="stname" v-colorg="bg2" />

自定义局部指令

加一个节点directives

var app = new Vue({
el: "#app",
data: {
co:{
color:'pink',
color2:'red'
}
},
methods: {

},
//局部 v-focuc focuc是自定义
directives: {
//获取焦点
focuc: {
// 指令的定义 不带参数
inserted: function (el) {
el.focus();
},
},
//带参数
col:{
bind: function (el,bindc){
el.style.color=bindc.value.color
}
}
},
});

使用

姓名:<input type="text" v-model.lazy="stname"  v-col="co"/><br />

计算属性computed

计算属性和直接调方法区别: 计算属性有缓存 调用多次只执行一次 方法调用多少次就执行多少次

计算属性可以直接赋值 不需要在定义

官网的一句话:对于任何复杂逻辑,你都应当使用计算属性。

methods方法和computed计算属性,两种方式的****最终结果确实是完全相同

界面

<div id="app">
<h1>计算属性:{{msg}}</h1>
<h1>计算属性:{{msg2}}</h1>

<input type="text" v-model="nr"/>
<h1>计算属性:{{zhuan}}</h1>
<h1>方法{{msg3()}}</h1>
</div>

js

var app = new Vue({
el: "#app",
data: {
msg: '你好',
nr:''
},
methods: {
msg3:function(){
console.log('方法...')
return this.nr.substring(0,1).toUpperCase()+this.nr.substring(1).toLowerCase();
}
},
//计算属性
computed:{
//计算属性 反转
msg2: function(){
return this.msg.split('').reverse().join('');
},
//首字母大小 后面小写
zhuan:function(){
console.log("计算中...")
return this.nr.substring(0,1).toUpperCase()+this.nr.substring(1).toLowerCase();
}
}
});

侦听器watch

侦听器 计算属性 方法的区别

计算属性和方法的区别:
计算属性有缓存 调用多次只执行一次
方法调用多次就执行多次
侦听器有缓存 computed生成一个新的数据,直接渲染至视图中,watch是修改本身已经存在的数据

界面

<div id="app">
<h1>全名:{{fullname}}</h1>
</div>

js

var app=new Vue({
el:"#app",
data: {
firstname: "bill",
lastname: "盖字",
},
//监听某个变量 改变后显示
watch: {
//val就是改后的
firstname:function(val){
this.fullname=val+this.lastname;
},
lastname:function(val){
this.fullname=this.firstname+val;
}
},
computed:{
fullname:function(){
return this.firstname+this.lastname;
}
}
});

侦听器 Ajax调用

<!DOCTYPE html>
<html lang="en">
<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">
<title>Document</title>
<script src="js/vue.js"></script>
<script src="js/jquery-1.10.2.js"></script>
<style>
.pe{
color:pink;
}
.re{
color:red;
}
</style>
</head>
<body>
<div id="app">
<input type="text" placeholder="请输入名字" v-model.lazy="usname"/>
<span :class="c1">{{tip}}</span>
</div>
</body>
<script>
var app=new Vue({
el:'#app',
data: {
usname:'',
tip:'',
c1:''
},
watch:{
//监听usname usname要监听的变量
usname:function(val){
this.tip="正在验证..."
//ajax请求
this.checkName(val);
}
},
methods:{
//this.tip的话 this就不是vue
checkName:function(value){
//检查是否合法 定时器 使用ajax settimeout改成ajax请求
//let that=this 保存起vue的this
setTimeout(function(){
if(value=="admin"){
app.c1="re";
app.tip="名字不合法!";
}else{
app.c1="pe";
app.tip="名字合法!";
}
},2000)
}
}
});
</script>
</html>

过滤器

| 隔开使用

//过滤器 处理插值表达式和属性

​ //1、可以用于插值表达式和属性绑定

​ //2、支持级联操作

全局过滤器

html

<h3>msg:{{msg | upper}}</h3>

js

//全局过滤器 upper是自定义的过滤器的名字   使用插值{{原变量 | 过滤器的名字}} 属性的话就不要{{}} 
Vue.filter('upper',function(val){
//首字母大写 其他的小写 val就是原来变量
return val.substring(0,1).toUpperCase()+val.substring(1).toLowerCase();
})

局部过滤器

var app=new Vue({
el:'#app',
data: {
msg:''
},
watch:{

},
methods:{

},
//局部过滤器
filters:{
upper2:function(val){
return val.substring(0,1).toUpperCase()+val.substring(1).toLowerCase();
}
}
});

过滤器带参数

界面

<div id="app">
<h1>原来:{{newtime}}</h1>
<h1>{{newtime | formattime('yyyy-MM-dd HH:mm:ss')}}</h1>
</div>

js

//过滤器带参数
Vue.filter('formattime', function(val,cf){
//val=newtime
if(cf=='yyyy-MM-dd HH:mm:ss'){
let y=val.getFullYear();
let m=val.getMonth()+1;
let d=val.getDate();
let s=val.getHours();
let f=val.getMinutes();
let miao=val.getSeconds();
return `${y}-${m}-${d} ${s}:${f}:${miao}`;
}
})
var app=new Vue({
el:'#app',
data: {
newtime:new Date(),
},
watch:{

},
methods:{

}
});

生命周期

8个

<!DOCTYPE html>
<html lang="en">
<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">
<title>Document</title>
<script src="js/vue.js"></script>
<script src="js/jquery-1.10.2.js"></script>
</head>
<body>
<div id="app">
<h1>测试 生命周期8个</h1>
<h3>{{message}}</h3>
<button type="button" @click="change">改变</button>
</div>
</body>
<script>
var app=new Vue({
el:"#app",
data: {
message:'hello vue!'
},
methods:{
change: function() {
this.message="hello word!";
},
change2(){
console.log("方法也可以这样写!");
}
},
beforeCreate: function() {
console.log("创建前.....")
console.log(this.message);//undefined
console.log(this.$el);//undefined
},
created(){
console.log("创建完成....");
console.log(this.message);//有
console.log(this.$el);//undefined
},
beforeMount(){
//dom放进去 变量没有解析 事件没有解析
console.log("挂载前....");
console.log(this.message); //信息
console.log(this.$el);//dom的div
},
mounted(){
//dom放进去 变量有解析 事件有解析
console.log("挂载完成....");
console.log(this.message); //信息
console.log(this.$el);//dom的div
},
beforeUpdate(){//虚拟的DOM
//操作后更新 改变了message
console.log("更新前....");
console.log(this.message); //信息
console.log(this.$el);//dom的div
},
updated(){
//操作后更新后
console.log("更新后....");
console.log(this.message); //信息
console.log(this.$el);//dom的div
},
beforeDestroy(){//app.$destroy() 执行销毁
//销毁前
console.log("销毁前....");
console.log(this.message); //信息
console.log(this.$el);//dom的div
},
destroyed(){
//销毁完成
console.log("销毁完成....");
console.log(this.message); //信息
console.log(this.$el);//dom的div
}
});
</script>
</html>

数组变化

<!DOCTYPE html>
<html lang="en">
<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">
<title>Document</title>
<script src="js/vue.js"></script>
<script src="js/jquery-1.10.2.js"></script>
</head>
<body>
<div id="app">
<pre>
push 改变原数组
pop 尾部移除 改变原数组
slice 不改变原数组 返回值改变
</pre>
<input type="text" v-model="mess">
<button type="button" @click="add">添加</button>
<button type="button" @click="del">pop删除</button>
<button type="button" @click="del2">splice删除</button>
<ul>
<li v-for="item in fruit">{{item}}</li>
</ul>
</div>
</body>
<script>
var app=new Vue({
el:'#app',
data: {
mess:'',
fruit:["apple", "orange","banane","orange2","banane2"]
},
methods:{
add(){
this.fruit.push(this.mess);
this.mess="";
},
del(){
this.fruit.pop();
},
del2(){
this.$set(app.fruit,1,"柠檬");
// var ok=this.fruit.slice(1,3);
// console.log(ok);
}
}
});
// app.$set(app.fruit,1,"柠檬");//动态改变
</script>
</html>

组件

组件注意事项

1、data是一个函数
2、组件模板必须是根元素
3、组件的模板内容可以是模板字符串

组件命名注意事项

如果是使用驼峰命名法的组件 在根组件里面无法使用 多个单词只能用- 横杠
驼峰命名法的组件只能在template模板中使用 使用时直接名字
如果组件的名字 有多个单词的时候 如:HelloWord 那么界面组件就是 Hello-Word

全局组件的注册

//组件的注册 button-unter:组件的名字  
Vue.component('button-unter',{
data(){
return {
count:0
}
},
//template:'<button @click="count++">计数 {{ count }} </button>'
template:'<button @click="click">计数 {{ count }} </button>',
methods:{
click(){
this.count++;
}
}
})

使用

<button-unter></button-unter>

局部组件注册

使用

<Hello-Word></Hello-Word>

vue

var helloword={
data(){
return {
name:'张三'
}
},
template:`<h3>我是{{name}}</h3>`
}

var app=new Vue({
el:'#app',
data:{

},
//可以多个
components:{
"hello-word":helloword
}
})

父传子组件(传参)

可以传的类型:String number boolean array Object

注意

如果使有样式的话可以直接赋值
或者照下面赋值

示例1

定义样式
.cf{
border-bottom: 1px solid red;
}
原来赋值 :class={cf:iscf} //iscf实际上是最大的父元素赋值的true || false
组件赋值::class="{cf:items.pbool}" //items.pbool是父元素的赋值 同样为 true|| false

界面

//bolis是自定义的属性名 在组件中接收 title是父元素传的值
<blog2-post :bolis="title"></blog2-post>

vue

//博客组件
Vue.component('blog2-post',{
data(){
return {
postFontSize:16
}
},
//bolis就是组件上的属性
props:['bolis'],
template:`<div>
<div v-for="items in bolis" :class="{cf:items.pbool}">
<h3>标题:{{items.title}}</h3>
<p :style="{ fontSize: postFontSize + 'px' }">内容:{{items.context}}</p>
<p>内容图片:
<img v-for="ite in items.blogimg" :src="ite" width="300px" height="200px" />
</p>
<p>博龄:{{items.age+10}}</p>
<p>鸣谢:
<ul >
<li v-for="it in items.help">{{it}}</li>
</ul>
</p>
<p>作者信息:
<ul>
<li>姓名:{{items.authinfo.name}}</li>
<li>创建时间:{{items.authinfo.crtime}}</li>
</ul>
</p>
<button @click="postFontSize+=10">放大字体</button>
</div>
</div>`
})

var app=new Vue({
el:'#app',
data:{
title:[
{
title:"博客1",
context:"博客1内容",
age:19,
pbool:true,
help:["auth1","auth9","auth2","auth5"],
authinfo:{name:"auth2",crtime:'2021-10-11'},
blogimg:['img/11.webp','img/12.webp']
},
{
title:"博客2",
context:"博客2内容",
age:10,
pbool:true,
help:["auth1","auth9","auth2","auth5"],
authinfo:{name:"auth1",crtime:'2021-10-11'},
blogimg:['img/11.webp']
},
{
title:"博客3",
context:"博客3内容",
age:15,
pbool:true,
help:["auth1","auth9","auth2","auth5"],
authinfo:{name:"auth3",crtime:'2021-10-11'},
blogimg:['img/11.webp','img/12.webp','img/22.webp']
},
{
title:"博客4",
context:"博客3内容",
age:20,
pbool:true,
help:["auth1","auth9","auth2","auth5"],
authinfo:{name:"auth4",crtime:'2021-10-11'},
blogimg:['img/11.webp','img/12.webp','img/22.webp','img/3210.webp']
}
]
}
})

效果 实际上就是循环了多个博客

image

子传父组件

$emit(‘自定义名字’,值) 这个自定义名字需要在使用组件时组成@自定义名字=“方法名”

方法是父类的方法 值是需要传递的值

子组件调用事件通知父组件
1、子组件模板绑定事件$emit('.....')
2、子元素标签绑定对应的事件,事件名与$emit括号内的名称一样
3、最后在vue对象中添加方法执行对应的操作

子组件传值
this.$emit('bigtext',10)
父组件接收子组件的值
handler(num){
this.fons+=10;
console.log("调用!"+num);
}

界面

<menu-item :he="help" @bigtext="handler"></menu-item>

组件

//菜单组件 子组件
Vue.component('menu-item',{
data(){
return {

}
},
//单向数据流
props:['he'],
template:`
<div >
<ul>
<li v-for="item in he">{{item}}</li>
</ul>
<button @click="test">改变</button>
</div>
`,
// <button @click="$emit('bigtext')">改变</button>
methods:{
//按钮点击方法
test(){
// this.he[0]="JK";
//调用方法
this.$emit('bigtext');
//传值
this.$emit('bigtext',10)
}
}
})
var app=new Vue({
el:"#app",
data: {
help:["auth1","auth9","auth2","auth5"],
pmsg:'父组件',
fons:16
},
methods:{
handler(num){
this.fons+=10;
console.log("调用!"+num);
}
}
});

兄弟组件传值

兄弟之间传值 
创建中央处路由 var hub=new Vue();
通知相对的组件jerry-event:是兄弟的挂载完成的name hub.$emit("jerry-event",2)
自己挂载完成后监听自己的
mounted(){
hub.$on("tom-event",(value)=>{
this.num+=value;
})
}

界面

<tom-com></tom-com>
<jerry-com></jerry-com>

vue

//事件中心 帮助传值 中央路由
var hub=new Vue();

//tom组件
Vue.component("tom-com",{
data(){
return {
num:0
}
},
template:`
<div>
<h3>tom={{num}}</h3>
<button type="button" @click="changjerr">改Jerry</button>
</div>
`,
methods: {
changjerr(){
//jerry的挂载监听的name 2:需要传的值
hub.$emit("jerry-event",2)
}
},
//挂载完成 都监听自己的
mounted(){
//tom-event名字自定义
hub.$on("tom-event",(value)=>{
this.num+=value;
})
}
})

//jerry
Vue.component("jerry-com",{
data(){
return {
num:0
}
},
template:`
<div>
<h3>jerry={{num}}</h3>
<button type="button" @click="changtom">改Tom</button>
</div>
`,
methods: {
changtom(){
hub.$emit("tom-event",3)
}
},
mounted(){
hub.$on("jerry-event",(value)=>{
this.num+=value;
})
}
})

var app=new Vue({
el:'#app',
data: {

}
});

image

插槽

//插槽===>父向子传网页内容 可以传任何东西 然后会替换到slot

​ //默认消息组件网页没有传值的话就是slot的默认值

界面

<alter-box></alter-box>

<alter-box>查询失败!</alter-box>

<alter-box>查询失败!
<img src="img/12.webp" width="400px" height="200px"/>
<button>点击</button>
</alter-box>

vue

Vue.component('alter-box',{
data(){
return {

}
},
template:`<div id="d1">
<h1>错误消息</h1>
<slot >默认消息</slot>
</div>`,
})
var app=new Vue({
el:"#app",
data:{

}
});

具名插槽(可以起名字)

通过名字指定显示的内容

界面

<base-layout>
<div slot="header" id="header">头部</div>
<div slot="main" id="main">中部</div>
<div slot="footer" id="footer">尾部</div>
</base-layout>

<base-layout>
<template slot="header" >
<h1>头部</h1>
<img src="img/11.webp" width="400px" height="200px"/>
</template>
<template slot="main" >
<h1>中部</h1>
<img src="img/12.webp" width="400px" height="200px"/>
</template>
<template slot="footer" >
<h1>尾部</h1>
<img src="img/22.webp" width="400px" height="200px"/>
</template>
</base-layout>

vue

Vue.component("base-layout",{
data(){
return {

}
},
//name自定义名字
template:`<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot name="main"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>`,

})

var app=new Vue({
el:'#app',
data:{

}
});

效果

image

作用域插槽

v-slot==slot-scope

子组件传值给父组件

示例1

界面

<div id="app">
<son-com>
<template v-slot="slotProps">
<h1>这是父组件的元素</h1>
<h1>子组件元素变量:{{slotProps.user.name}}</h1>
<h1>子组件元素变量:{{slotProps.user.nickname}}</h1>
</template>
</son-com>
</div>

vue

<script>
/**
* 作用域插槽实际上就是 子组件变量传父组件
* 传值:v-slot="自定义名字" {{自定义名字.slot传的属性name.属性}}
*/
//作用域插槽:子传父
Vue.component("son-com",{
data(){
return {
heros:{
name:"李白",
nickname:"剑仙"
}
}
},
template:`<div>
<h1>作用域插槽</h1>
<slot :user="heros"></slot>
</div>`,
})
var app=new Vue({
el:"#app",
data: {
}
});
</script>

示例2

界面

<!--形状列表-->
<my-list title="形状列表" :items="shapes">
<template v-slot:default="slotProps">
<div>{{slotProps.item.name}} <small>(sides:{{slotProps.item.sides}})</small></div>
</template>
</my-list>
<my-list title="颜色" :items="color">
<template v-slot:default="slotProps">
<div><span class="swatch" :style="{backgroundColor:slotProps.item.hex}"></span> {{slotProps.item.name}}</div>
</template>
</my-list>

<my-list title="英雄列表" :items="heros">
<template v-slot:default="slotProps">
<div>{{slotProps.item.name}} <img width="40px" height="40px" :src="'img/'+slotProps.item.img"/> {{slotProps.item.sex}}</div>
</template>
</my-list>

vue

<script>
Vue.component("my-list",{
data(){
return {
}
},
props:['title', 'items'],
template:`<div class="my-list">
<div class="title">{{title}}</div>
<div class="list">
<div class="list-item" v-for="item in items">
<slot :item="item"></slot>
</div>
</div>
</div>`,
})
var app=new Vue({
el:"#app",
data: {
shapes:[
{name:'正方形',sides:4},
{name:'三角形',sides:3},
{name:'六边形',sides:6}
],
color:[
{name:'黄色',hex:'#FD403F'},
{name:'绿色',hex:'#229954'},
{name:'粉色',hex:'#9B59B6'}
],
heros:[
{name:'temo',img:'11.webp',sex:'男'},
{name:'德玛',img:'12.webp',sex:'男'},
{name:'女警',img:'22.webp',sex:'女'}
]
}
});
</script>

效果

image

路由

路由需要先引用vue 在引用路由

可以不用挂载组件

路由

下载路由

npm i vue-router@3.0.2 然后拷贝js

原生模拟路由

<!--组件占位符 :is定义属性 comname是父组件定义的属性 改变组件-->
<component :is="comname"></component>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<style>
a{
margin-right: 10px;
text-decoration: none;
}
</style>
</head>
<body>
<div id="app">
<a href="#/cj">财经</a><a href="#/ty">体育</a><a href="#/xw">新闻</a><a href="#/js">军事</a>
<hr>
<!--组件占位符 :is定义属性 comname是父组件定义的属性 改变组件-->
<component :is="comname"></component>
</div>
</body>
<script>

var cj={
template:`<h1>财经</h1>`
}
var ty={
template:`<h1>体育</h1>`
}
var xw={
template:`<h1>新闻</h1>`
}
var js={
template:`<h1>军事</h1>`
}
Vue.component('',{
})
var app=new Vue({
el:"#app",
data:{
comname:''
},
components:{
cj,ty,xw,js
}
})
window.onhashchange = function(){
let path=location.hash.substring(1);
console.log(path);
switch (path) {
case "/cj":
app.comname=cj;
break;
case "/ty":
app.comname=ty;
break;
case "/xw":
app.comname=xw
break;
case "/js":
app.comname=js
break;
}
}
</script>
</html>

路由基本使用

 <!--运行后转换成a标签-->
<router-link to="/cj">财经</router-link>
<!--路由占位符-->
<router-view></router-view>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<style>
a{
margin-right: 10px;
text-decoration: none;
}
</style>
</head>
<body>
<div id="app">
<!--运行后转换成a标签-->
<router-link to="/cj">财经</router-link>
<router-link to="/ty">体育</router-link>
<router-link to="/xw">新闻</router-link>
<router-link to="/js">军事</router-link>
<a href="#/cj">财经</a><a href="#/ty">体育</a><a href="#/xw">新闻</a><a href="#/js">军事</a>
<hr>
<!--路由占位符-->
<router-view></router-view>
</div>
</body>
<script>
var cj={
template:`<h1>财经</h1>`
}
var ty={
template:`<h1>体育</h1>`
}
var xw={
template:`<h1>新闻</h1>`
}
var js={
template:`<h1>军事</h1>`
}
var router=new VueRouter({
routes:[
{path:"/cj",component:cj},
{path:"/ty",component:ty},
{path:"/xw",component:xw},
{path:"/js",component:js},
]
})
var app=new Vue({
el:"#app",
data:{
comname:''
},
components:{
cj,ty,xw,js
},
router:router
})
</script>
</html>

路由重定向

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<style>
a{
margin-right: 10px;
text-decoration: none;
}
</style>
</head>
<body>
<div id="app">
<!--运行后转换成a标签-->
<router-link to="/cj">财经</router-link>
<router-link to="/ty">体育</router-link>
<router-link to="/xw">新闻</router-link>
<router-link to="/js">军事</router-link>
<a href="#/cj">财经</a><a href="#/ty">体育</a><a href="#/xw">新闻</a><a href="#/js">军事</a>
<hr>
<!--路由占位符-->
<router-view></router-view>
</div>
</body>
<script>
var cj={
template:`<h1>财经</h1>`
}
var ty={
template:`<h1>体育</h1>`
}
var xw={
template:`<h1>新闻</h1>`
}
var js={
template:`<h1>军事</h1>`
}
var router=new VueRouter({
routes:[
//路径重定向
{path: "/",redirect:'/cj'},
{path:"/cj",component:cj},
{path:"/ty",component:ty},
{path:"/xw",component:xw},
{path:"/js",component:js},
]
})
var app=new Vue({
el:"#app",
data:{
comname:''
},
components:{
cj,ty,xw,js
},
router:router
})
</script>
</html>

嵌套路由

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<style>
a{
margin-right: 10px;
text-decoration: none;
}
</style>
</head>
<body>
<div id="app">
<!--运行后转换成a标签-->
<router-link to="/index">首页</router-link>
<router-link to="/reg">注册</router-link>
<hr>
<!--路由占位符-->
<router-view></router-view>
</div>
</body>
<script>
var index={
template:`<h1>主页</h1>`
}
var regis={
template:`<div>
<h1>用注册</h1>
<router-link to="/tab1">table1</router-link>
<router-link to="/tab2">table2</router-link>
<hr>
<router-view></router-view>
</div>`
}
var tab1={
template:'<h1>表格1</h1>'
}
var tab2={
template:'<h1>表格2</h1>'
}
var router=new VueRouter({
routes:[
//路径重定向
{path: "/",redirect:'/index'},
{path:"/index",component:index},
{path:"/reg",component:regis,
//子节点 子路由的 需要多一个子节点
children:[
{path: '/tab1',component:tab1 },
{path: '/tab2',component:tab2 },
]}
]
})
var app=new Vue({
el:"#app",
data:{
comname:''
},
router:router
})
</script>
</html>

动态路由传值

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<style>
a{
margin-right: 10px;
text-decoration: none;
}
</style>
</head>
<body>
<div id="app">
<!--运行后转换成a标签-->
<router-link to="/index/1">首页</router-link>
<router-link to="/index/2">首页</router-link>
<router-link to="/reg">注册</router-link>
<hr>
<!--路由占位符-->
<router-view></router-view>
</div>
</body>
<script>
var index={
//ID是规则里面声明的变量
template:`<h1>{{$route.params.id}}主页</h1>`
}
var regis={
template:`<div>
<h1>用注册</h1>
</div>`
}
var router=new VueRouter({
routes:[
//路径重定向
{path: "/",redirect:'/index/:id'},
{path:"/index/:id",component:index},
{path:"/reg",component:regis}
]
})
var app=new Vue({
el:"#app",
data:{
comname:''
},
router:router
})
</script>
</html>

动态路由传值2

需要传值时 在规则中加 props:true

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<style>
a{
margin-right: 10px;
text-decoration: none;
}
</style>
</head>
<body>
<div id="app">
<!--运行后转换成a标签-->
<router-link to="/index/1">首页</router-link>
<router-link to="/index/2">首页</router-link>
<router-link to="/reg/temo/pd">注册</router-link>
<router-link to="/reg2">注册2</router-link>

<router-link to="/reg3/18">注册2</router-link>
<hr>
<!--路由占位符-->
<router-view></router-view>
</div>
</body>
<script>
var index={
props:['id'],
template:`<h1>{{id}}主页</h1>`
}
var regis={
props:['name','kn'],
template:`<div>
<h1>用注册{{name}} {{kn}}</h1>
</div>`
}
var regis2={
props:['age','sex'],
template:`<div>
<h1>用注册{{age}} {{sex}}</h1>
</div>`
}
var regis3={
props:['age','sex','id'],
template:`<div>
<h1>用注册{{age}} {{sex}} {{id}}</h1>
</div>`
}
var router=new VueRouter({
routes:[
//路径重定向
{path: "/",redirect:'/index/1'},
//路径参数
{path:"/index/:id",component:index,props:true},
//路程带参数
{path:"/reg/:name/:kn",component:regis,props:true},
//对象带参数
{path:"/reg2",component:regis2,props: {
age:19,
sex:'男'
}},
//对象传值 路径和对象带参数
{path:"/reg3/:id",component:regis3,props:router=>({
age:19,
sex: '男',
id:router.params.id
})},
]
})
var app=new Vue({
el:"#app",
data:{
comname:''
},
router:router
})
</script>
</html>

路由传值3

  • query传参
    字符串
<router-link :to="`/home?text=${text}`" >  首页</router-link>

对象

<router-link :to="{ path: '/home', query: { text: text } }">  首页</router-link>

获取

this.$route.query.text
  • params 传参
    路由器声明 params 传参
{  name: 'shouye',  path: 'home/:text', //字符串形式传参时需加占位符告知路由器,此时是参数  component: Home,},

字符串

<router-link :to="`/home${text}`">  首页</router-link>

对象

<router-link :to="{ name: 'shouye', params: { text: text } }">  首页</router-link>

获取

this.$route.params.text
* 注意
    * 字符串形式传参时需加占位符告知路由器, 在路径后面是参数
    * path对应的是 query属性,name 对应的是 params 属性 
  • 编程式路由导航
    写法
this.$router.push({  path: "/home",  query: {    text: this.text,  },});

导航到新路由

//编程式 可以在点击时使用
this.$router.push("/userinfo");

返回上一步回退

this.$router.go(-1);

后台管理示例

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<style>
.d1{
position: absolute;
width: 947px;
height: 500px;
background: #FFFFFF;
border: 1px solid black;
}
header{
height: 50px;
background-color: #545c64;
line-height: 50px;
text-align: center;
font-size: 24px;
color: #fff;
}
footer{
height: 40px;
line-height: 40px;
background-color: #888;
position: absolute;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
}
#d2{
height: 410px;
width: 100%;
background-color:#545C63;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
ul li{
height: 45px;
line-height: 45px;
background-color: #a0a0a0;
color: #fff;
text-align: center;
border-bottom: 1px solid #fff;
}
#d3{
float: left;
width:150px;
height:100%;
}
#main{
float:left;
width:797px;
height:100%;
text-align: center;
background-color: white;
}
table {
width: 789px;
border-collapse: collapse;
margin-left: 4px;
margin-right: 4px;
overflow:hidden;white-space:nowrap;
}
th {
background-color: #ddd;
}
td,
th {
border: 1px solid #eee;
line-height: 35px;
font-size: 12px;
}
ul a{
text-decoration: none;
color: #F2F2F2
}
button {
height: 30px;
background-color: #ecf5ff;
border: 1px solid lightskyblue;
font-size: 12px;
padding: 0 20px;
}
</style>
</head>
<body>
<div id="app">
<router-view></router-view>
</div>
</body>
<script>
var main={
template:`<div class="d1">
<header>
后台管理系统
</header>
<div id="d2">
<div id="d3">
<ul>
<li><router-link to="/user">用户管理</router-link></li>
<li><router-link to="/jur">权限管理</router-link></li>
<li><router-link to="/shop">商品管理</router-link></li>
<li><router-link to="/order">订单管理</router-link></li>
<li><router-link to="/sys">系统设置</router-link></li>
</ul>
</div>
<router-view></router-view>
</div>
<footer>
版权所有
</footer>
</div>`,
}
var user={
data(){
return{
userli:[
{id:1,name:'张三',age:10},
{id:2,name:'李四',age:20},
{id:3,name:'王五',age:30},
{id:4,name:'赵柳',age:40}
]
}
},
template: `<div id="main">
<h1>用户管理</h1>
<table>
<tr>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>操作</th>
</tr>
<tr v-for="lis in userli">
<td>{{lis.id}}</td><td>{{lis.name}}</td><td>{{lis.age}}</td><td><a href="#" @click.prevent="ope(lis.id)">操作</a></td>
</tr>
</table>
</div>`,
methods:{
ope(id){
this.$router.push("/userinfo/" + id)
}
}
}
var userinfo ={
props:['id'],
template: `<div id="main">
<h1>用户ID:{{id}}</h1>
<button @click="repre">后退</button>
</div>`,
methods:{
repre(){
this.$router.go(-1);
}
}
}
var jur={
template: `<div id="main">
<h1>权限管理</h1>
</div>`
};
var shop={
template: `<div id="main">
<h1>商品管理</h1>
</div>`
};
var order={
template: `<div id="main">
<h1>订单管理</h1>
</div>`
};
var sys={
template: `<div id="main">
<h1>系统设置</h1>
</div>`
};
var router=new VueRouter({
routes:[
//先打开一个组件 然后重定向到user
{path:"/",component:main,redirect:'/user',
children:[
{path:"/user",component:user},
{path:"/userinfo/:id",component:userinfo,props:true},
{path:"/jur",component:jur},
{path:"/shop",component:shop},
{path:"/order",component:order},
{path:"/sys",component:sys}
]},
]
})
var app=new Vue({
el:"#app",
data: {
},
router
})
</script>
</html>

image

命名路由

界面

//name:'路由规则里面调用的'
<router-link to="/user/88">用户</router-link>
<router-link :to="{name:'yh',params:{
id:19
}}">用户</router-link>
<router-link to="/reg">注册</router-link>
<router-view></router-view>

路由规则添加命名

//name:xxx
var router=new VueRouter({
routes:[
{path:'/',redirect:'/user/:id'},
{name: 'yh',path:'/user/:id',component:User,props:true},
{path:'/reg',component:reg},
]
})

传值

<router-link :to="{name:'yh',params:{
id:19
}}">用户</router-link>

//对应的组件获取
props:['id']

编程式导航

跳转

var User={
props:['id'],
template:`<div>
<h1>用户组件 {{id}}</h1>
<button @click="add">添加</button>
</div>`,
methods:{
add(){
//编程是跳转
this.$router.push('/reg');
}
}
}

跳转传值

var reg={
template:`<div>
<h1>用户注册</h1>
<button @click="gouser">去用户</button>
</div>`,
methods:{
gouser(){
//跳转传值
this.$router.push({
name: 'yh',
params: {
id:109
}
})
}
}
}

获取值
props:['id']

回退

this.$router.go(-1);

Vue脚手架

使用UI创建项目

安装脚手架

npm install -g @vue/cli

查看版本

vue --version

使用UI

vue ui

image

选择路径创建项目

image

iShot2022-03-16 09.02.02

选择预设 手动

iShot2022-03-16 09.02.41

选择功能 Babel(优化高版本代码浏览器不兼容)、Router(路由)、Linter/Formatter(代码规范)、使用配置文件

iShot2022-03-16 09.06.22

选择版本 语法检查+标准配置

iShot2022-03-16 09.08.27

生成的项目结构

image

使用命令创建项目

如果安装了脚手架的情况下就执行下面操作

创建项目

//在需要创建项目的目录下输入shell命令

vue create 名字

选择对应的内容 上下选择 空格选中

iShot2022-03-16 09.55.17

运行项目

//端口是8080 
npm run serve

//也可以在UI界面中选择运行项目

image

启动UI界面

//端口是8000
vue ui

去掉语法规范

vue.config.js

lintOnSave:false

image

Vue项目目录说明

+ Vuecli01
+ node_modules(存放第三方模块)
+ public(存放静态文件)
- favicon.ico(图标)
- index.html(页面模板)
+ src(我们自己写的文件一般放在这个文件夹下)
+ assets(存放资源文件 图片)
+ components(存放公共组件)
+ router(存放路由)
+ views(存放视图组件)
- App.vue(页面入口文件)
- main.js(程序入口文件)

.$mount==el:'#app' 项目入口

@是/src的别名

src/App.vue 根组件单文件 三部分 template、script、style

安装 Vetur vscode插件

示例

创建一个 /views/UserView.vue

<template>
<div>
<h1>V㛑 goods</h1>
<img alt="goods" src="../assets/3210.webp">
</div>
</template>
<script>
export default {
name: 'UserView'
}
</script>
<style>
img {
width: 600px;
height: 400px;
}
</style>

添加路由 router/index.js

{
path: '/user',
name: 'user',
component: UserView
}

image

效果

image

修改项目运行的端口

vue.config.js

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave:false,
//修改端口
devServer:{
open: true,
port:8090
}
})

使用命令下载Element UI

npm下载

npm i element-ui -S

main.js下面 引入element ui

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

image

示例table使用

<template>
<div>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName">
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
</div>
</template>

<script>
export default {
name: "TableView",
methods:{
tableRowClassName({row, rowIndex}) {
if (rowIndex === 1) {
return 'warning-row';
} else if (rowIndex === 3) {
return 'success-row';
}
return '';
}
},
data(){
return{
tableData:[
{date:'2021-10-11',name:'张三',address:'上海市普陀区金沙江路 1518 弄'},
{date:'2021-10-17',name:'张三',address:'上海市普陀区金沙江路 1518 弄'},
{date:'2021-10-14',name:'张三',address:'上海市普陀区金沙江路 1518 弄'},
{date:'2021-10-13',name:'张三',address:'上海市普陀区金沙江路 1518 弄'},
{date:'2021-10-12',name:'张三',address:'上海市普陀区金沙江路 1518 弄'}
]
}
}
}
</script>
<style>
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>

使用UI下载Element UI插件

这里下载的插件属于按需引入需要什么引入什么

选择好项目 选择插件 添加插件 搜索element ui 选中点安装

iShot2022-03-17 09.13.22

配置插件

iShot2022-03-17 09.15.10

安装后会多一个文件夹 src/plugins 下面有element.js

image

在element.js下按需引入

import Vue from 'vue'
//引入按钮 和 表格
import {Button, Table, TableColumn} from 'element-ui'
//使用引入的内容
Vue.use(Button)
Vue.use(Table)
Vue.use(TableColumn)

使用示例

<template>
<div>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName">
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: "TableView",
methods: {
tableRowClassName({row, rowIndex}) {
if (rowIndex === 1) {
return 'warning-row';
} else if (rowIndex === 3) {
return 'success-row';
}
return '';
}
},
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄',
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄',
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}]
}
}
}
</script>
<style >
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>

注意 下面这种情况是因为没有引入需要的内容

image

Element UI 表单使用

安装less

//安装less
npm install less less-loader@7.0.0 -D
<template>
<div class="login_co">
<div class="content">
<div class="content_input">
<div class="title">
<p>管理员登录</p>
</div>
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm">
<el-form-item prop="username">
<el-input type="text" clearable v-model="ruleForm.username" autocomplete="off" placeholder="用户名"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" clearable show-password v-model="ruleForm.password" autocomplete="off" placeholder="密码"></el-input>
</el-form-item>
<div class="content_button">
<el-button type="primary" @click="login">登录</el-button>
<el-button @click="replace">重置</el-button>
</div>
</el-form>
</div>
</div>
</div>
</template>
<script>
import '@/assets/css/style.css'
import '@/assets/js/ribbon.js'
export default {
name: "Login",
data(){
return{
ruleForm: {
username: '',
password:'',
},
rules: {
//用户名验证
username: [
{required: true, message: '请输入用户名', trigger: 'blur'},
],
//密码
password:[
{required: true, message: '请输入密码', trigger: 'blur'},
]
}
}
},
methods:{
//登入
login(){
//验证是否满足规则 true:满足 false:不满足 ruleForm是上面定义的:model
this.$refs.ruleForm.validate(async (valid) => {
if(valid!=true){
this.$message({
showClose: true,
message: '请检查账号密码!',
type: 'warning'
});
return;
}else{
//axios请求
let ret=await this.$http.post('login',this.ruleForm);
console.log(ret.data.token);
}
})
},
//重置
replace(){
this.$refs.ruleForm.resetFields();
}
}
}
</script>

<style lang="less" scoped>
.login_co{
//background-color:#2b4b6b;
height:100%;
}
</style>

main.js

import '@/assets/css/global.css'
import axios from 'axios'
//axios挂Vue
Vue.prototype.$http =axios;
axios.defaults.baseURL="http://47.101.177.78:8888/api/private/v1/"
//拦截器
axios.interceptors.response.use(function (res){
return res.data;
})

导入全局样式

在main.js下面导入

import '@/assets/css/global.css'

局部组件导入样式

在组件<script></script>里面导入

image

下载挂载axios

import axios from 'axios'
//axios挂Vue
Vue.prototype.$http =axios;
axios.defaults.baseURL="http://47.101.177.78:8888/api/private/v1/"

//使用

image

路由守卫

方法1

router/index.js
router导出

//路由守卫
//验证token; to:去哪里 from来自 next放行
router.beforeEach((to,from,next)=>{
//登入的时候 放行
if(to.path=='/login'){
next();
//判断token是否有值
}else if(sessionStorage.getItem('token')==null){
next({path:'/login'})
}else{
//下一步
next();
}
})

方法2

路由配置 meta需要鉴权 isauth随便定义的name 需要拦截的地方

{
path: 'front',
component: Front,
meta: { isAuth: true },
},
{
path: 'back',
component: Back,
meta: { isAuth: true },
},
router.beforeEach((to, from, next) => {
//isauth需要鉴权的地方
if (to.meta.isAuth) {
if (localStorage.getItem('isShow' === '1')) {
next();
} else {
alert('暂无权限观看');
}
} else {
next();
}
});

设置Token

//设置token
axios.interceptors.request.use(function (res){
let token=sessionStorage.getItem('token');
res.headers.Authorization=token;
return res;
})

Keep-alive 组件缓存

//注意 Front 是组件的名字
<keep-alive include="Front">
<router-view></router-view>
</keep-alive>

Vuex

getter state里面的只能在计算属性中解析

compued:{
...mapGetters('模块名',['变量名 || 方法名'])
}

mutations在方法中使用

methods:{

}

使用UI创建项目

iShot2022-03-29 08.43.07

命令下载vuex

npm i vuex

搭建vux运行环境

创建 /store/index.js 文件

 // 创建 /store/index.js 文件

import Vue from 'vue' //引入Vue
import Vuex from 'vuex' //引入Vuex
Vue.use(Vuex) //应用

export default new Vuex.Store({
actions:{}, //接受用户的事件
mutations:{}, //操作state中的数据
state:{}, //存放共享的数据
})
// main.js

import store from './store/index.js'

new Vue({
el:"#app",
render: h => h(App),
store:store
})

定义组件

AddNumber.vue

<template>
<div>
<h1>当前的计数(+):{{$store.state.count}} -- {{$store.getters.con}}</h1>
<button @click="add">加不传值</button>
<button @click="add2">加传值</button>
<button @click="add3">异步加N</button>
</div>
</template>
<script>
export default {
name: "AddNumber",
data(){
return{
/*num:this.$store.state.count 方法中获取*/
}
},
methods:{
add(){
//不推荐
/*this.$store.state.count++;*/
//推荐 触发store里面 mutations的方法
this.$store.commit('add');
},
add2(){
this.$store.commit('addn',5);
},
add3(){
this.$store.dispatch('add3',5);
}
}
}
</script>
<style scoped>
</style>

SubtractionNumber.vue

<template>
<div>
<h1>当前的计数(-):{{count}}</h1>
<button @click="hand">减不传值</button>
<button @click="hand2">减传值</button>
<button @click="hand3">异步减传值</button>
</div>
</template>
<script>
import {mapState} from 'vuex'
import {mapMutations} from 'vuex'
import { mapActions } from 'vuex'
export default {
name: "SubtractionNumber",
data(){
return{
}
},
//计算属性的获取
computed:{
//count是定义共享数据的变量名 count在store/index.js/state
...mapState(['count'])
},
methods:{
//sub subn是在store的mutations里面调用的方法名
...mapMutations(['sub','subn']),
//展开掉actions的异步方法 subn是actions里面的方法名
...mapActions(['asysubn']),
hand() {
//调用对应的方法 不传值
this.sub()
},
hand2(){
//传值
this.subn(5);
},
hand3(){
//异步
this.asysubn(10);
}
}
}
</script>
<style scoped>
</style>

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {//定义共享数据
count:0
},
getters: {
con(state){
if(state.count<=0){
return state.count=1
}else{
return state.count;
}
}
},
mutations: {//负责更新state里面的属性 是同步的
add(state){
state.count++;
},
addn(state,value){
state.count+=value;
},
sub(state){
state.count--;
},
subn(state,val){
state.count-=val;
}
},
actions: {//异步
add3(context,value){
//模拟
setTimeout(function(){
context.commit('addn',value);
},3000)
},
asysubn(context,value){
//模拟
setTimeout(function(){
context.commit('subn',value);
},3000)
}
},
modules: {
}
})

state获取共享属性

共享属性也就是 strore/index.js下的state下的属性

获取方式一

<h1>当前的计数(+):{{$store.state.count}}</h1>

获取方式二

使用计算属性

先导入
import {mapState} from 'vuex'

//计算属性的获取
computed:{
//count是定义共享数据的变量名 count在store/index.js/state
...mapState(['count'])
},

//使用
<h1>当前的计数(-):{{count}}</h1>

mutations同步修改共享属性

方法一

commit(‘在store/inde.js的 mutations里面定义的方法名’)

methods:{
add(){
//不推荐
/*this.$store.state.count++;*/
//推荐 触发store里面 mutations的方法
//不传值
this.$store.commit('add');
},
add2(){
//传值
this.$store.commit('addn',5);
}
}

store/index.js

mutations: {//负责更新state里面的属性 是同步的
add(state){
state.count++;
},
addn(state,value){
state.count+=value;
}
}

方法2

导入
import {mapMutations} from 'vuex'

methods:{
//sub subn是在store的mutations里面调用的方法名
...mapMutations(['sub','subn']),
hand() {
//调用对应的方法 不传值
this.sub()
},
hand2(){
//传值
this.subn(5);
}
}

store/index.js

mutations: {//负责更新state里面的属性 是同步的
sub(state){
state.count--;
},
subn(state,val){
state.count-=val;
}
}

actions异步修改共享属性

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
  • 注意
    • actions 能够提供 dispatch 方法实现异步操作
    • mutations 必须是同步函数
    • state 只能通过 mutations 配置的方法去修改

方法一

methods:{
add3(){
//add3是store/index.js下的actions下定义的方法的名字
this.$store.dispatch('add3',5);
}
}

store/index.js

mutations: {//负责更新state里面的属性 是同步的
addn(state,value){
state.count+=value;
}
},

actions: {//异步
add3(context,value){
//模拟
setTimeout(function(){
//addn实际上是mutations里面定义的方法名
context.commit('addn',value);
},3000)
}
}

方法二

导入
import { mapActions } from 'vuex'

methods:{
//展开掉actions的异步方法 subn是actions里面的方法名
...mapActions(['asysubn']),
hand3(){
//异步
this.asysubn(10);
}
}

store/inde.js

mutations: {//负责更新state里面的属性 是同步的
subn(state,val){
state.count-=val;
}
},
actions: {//异步
asysubn(context,value){
//模拟
setTimeout(function(){
//subn是mutations下面的方法的名字
context.commit('subn',value);
},3000)
}
},

store的计算属性getters

  • storestate 数据进行加工
    配置 store/index.js
getters: {
//当count改变的时候会*2
changeCount(state) {
return state.count * 2;
},
},

取值

<h1>加工后的计数:{{$store.getters.changecount}}</h1>

多组件共享数据

读取 store 的state中 countlist

<ul>
<li v-for="(i, index) in list" :key="index">{{ i.name }}</li>
</ul>
<button @click="xiajia">下架</button>
<h1>
当前计数为:<span>{{ count }}</span>
</h1>
  • 使用计算属性
computed: {
count() {
return this.$store.state.count;
},
list() {
return this.$store.state.list;
},
},

操作 store 的state

methods: {
xiajia() {
this.$store.commit("XIAJIA");
},
},

store/index.js

state: {
count:0,
list:[
{name: 'HTML'},
{name: 'Java'},
{name: 'C#'}
]
},
getters: {
},
mutations: {
XIANJIAO(state){
state.list.pop();
}
},
actions: {
add(context,value){
setInterval(()=>{
context.commit('ADD',value);
},1000)
},
reduce(context,value){
context.commit('REDUCT',value)
}
},
modules: {
}

模块使用

有许多数据需要共享时 需要进行模块化开发

如创建一个cart.js的模块

export default {
//开启命名空间
namespaced: true,
state: () => ({

}),
getters: {

},
mutations: {

}
}

store.js需要注册模块

import Vue from 'vue'
import Vuex from 'vuex'
import carmoudele from './cart.js'

Vue.use(Vuex);

export default new Vuex.Store({
modules:{
m_cart:carmoudele
}
})

使用cart.js模块

import {
mapState,
mapMutations
} from 'vuex' //一样需导入map。。。

computed: {
//m_cart是store里面注册的cart的模块名 cart是cart.js里面的变量
...mapState('m_cart', ['cart'])
}

Vue3

vue3的项目创建和vue2是一样的 最后版本选择3即可

项目文件介绍区别

  • 入口文件 main.js
    使用 createApp 解析模板,更加小巧
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App).use(store).use(router).mount('#app')
  • 组件 <template> 标签里可以插入多个根标签

setup

  • setup 函数是Vue3新的配置项
  • 是使用组合API的前提,数据、方法都要放到setup 函数里声明
  • 写法
 setup(){
let name = "HelloWordld";
let age = 18;
function f() {
alert(`你好,我是${name},今年${age}`);
};
return{
name,
age,
f
}
}

//使用
<template>
<h1>hello world</h1>
<h1>姓名:{{name}}</h1>
<h1>年龄:{{age}}</h1>
<button @click="f">打招呼</button>
</template>

实现数据的响应式–ref函数

定义

* 定义一个响应式的数据

基本类型数据

* 引入 `ref` 函数
import { ref } from "vue" 
* 创建一个包含响应式数据的引用对象( `ref` 对象) 
 let name = ref("张三");
let age = ref(18);

操作数据

function changePerson() {
name.value = "李四";
age.value = "19";
}

对象类型数据

创建一个包含响应式数据的引用对象( ref 对象)

//对象
let obj=ref({
id:10,
name:'农科'
})

操作数据

function ChangeObject(){
obj.value.id=228;
obj.value.name='王华'
}

注意

* 可以处理基本类型数据、数组或者对象类型的数据
    * 基本类型数据的响应式是通过 `Object.defineProperty()` 实现
    * 对象类型数据的响应式是通过 ES6 中的 `Proxy` 实现

实现数据的响应–reactive

可以把所有的定义到一个变量里面

  • 定义
    • 定义一个对象类型的响应式数据(不能处理基本类型数据)
  • 写法
    对象
let obj=reactive({
id:10,
name: "李四",
age:90
});
function objchnage(){
obj.name='王五'
};

数组

let nk=['苹果','香蕉','西瓜','葡萄'];
function objchnage(){
nk[1]='西瓜2';
};
  • reactiveref 不同点
    • 处理数据类型不同: ref 可以处理基本类型和对象(数组)类型数据,reactive 只能处理对象(数组)类型数据
    • 实现原理不同:ref 处理基本类型数据通过 Object.defineProperty() 实现,reactive 通过 Proxy 实现
    • 操作不同:ref 操作数据需要加 .value
  • 组件数据多时更加趋向使用 reactive

setup执行事件 和 2个参数

简介:详解setup函数的两个参数

  • 执行时机
    • 在生命周期函数 beforeCreate 之前执行一次,而且 setup 函数没有 this
  • 两个参数
    • props
      第一个参数接收父组件的值,是一个对象
export default {
props: ["mess"], //需要props声明才能在setup收到参数 mess是父组件的自定义属性
setup(props) {
console.log(props.mess);
},
};
* context
    * 上下文对象
    触发自定义事件
export default {
emits: ["xd"], //需要emits声明才能在setup中使用 xd是父组件上的自定义事件
setup(props, context) {
function clickMe() {
context.emit("xd", "子组件的值");//xd是父组件自定义事件 第二个参数是值
}
return {
clickMe,
};
},
};

setup中的计算属性

  • 定义
    • 通过已有的属性计算而来,跟vue2.x中的功能原理一样,使用方式有区别
  • 使用
    计算 ref 定义的响应式数据
const fullName = computed({
get() {
return firstName.value + "-" + lastName.value;
},
set(value) {
const arr = value.split("-");
firstName.value = arr[0];
lastName.value = arr[1];
},
});

计算reactive定义的响应式数据

person.fullName = computed({
get() {
return person.firstName + "-" + person.lastName;
},
set(value) {
const arr = value.split("-");
person.firstName = arr[0];
person.lastName = arr[1];
},
});

setup中的侦听器

ref侦听

  • 定义
    • 监听值的变化,执行相关的操作,跟vue2.x中的配置一样
  • 监听 ref 定义的数据
    • 基本类型
 // 监听一个ref定义的数据
watch(num, (newValue, oldValue) => {
console.log("num增加了", newValue, oldValue);
},{ immediate: true, deep: true });

// 监听多个ref定义的数据
watch([num, num1], (newValue, oldValue) => {
console.log("num增加了", newValue, oldValue);
});

reactive侦听

  • 监听 reactive 定义的数据
    • 使用
      监听对象类型
watch(numObj, (newValue, oldValue) => {
console.log("numObj变化了", newValue, oldValue);});

监听对象中的一个基本类型属性

watch(
() => numObj.a,
(newValue, oldValue) => {
console.log("numObj变化了", newValue, oldValue);
});

监听对象中的一些基本类型属性

watch([() => numObj.a, () => numObj.b], (newValue, oldValue) => {
console.log("numObj变化了", newValue, oldValue);});

监听对象中的对象类型属性

watch(
numObj.c,
(newValue, oldValue) => {
console.log("numObj.c变化了", newValue, oldValue);
}
);
  • 总结
    • 实现监听生效
      • ref 定义的数据
        • 基本类型数据作为监听值
        • 对象作为监听值,需要加 .value(用的少)
      • reactive 定义的数据
        • 对象作为监听值
        • 属性作为监听值,需要放在回调函数中
    • 注意
      • 如果监听 reactive 定义的对象,则无法正确输出 oldValue ,且深度监听是强制开启的,无法关闭 (vue3配置)

setup中的watchEffect函数

  • 定义
    • 在监听的回调函数中使用了属性,则监听该属性,不用在参数上指明监听哪个属性
      写法
watchEffect(() => {
//这里面有改变了则会监听
let xd = numa.value;
let xd1 = numb.value;
console.log("watchEffect函数执行了");
  • });与 watch 的区别
    • 属性监听区别:
      • watch 手动添加定向的监听属性
      • watchEffect 自动监听使用到的属性
    • 初始化执行:
      • watchEffect 会初始化执行一次
  • 建议开发中使用 watch 监听,逻辑简单、依赖属性少的场景可以使用 watchEffect

toRef和toRefs函数

  • toRef
    • 定义
      • 创建一个 ref 对象,其 value 值指向另一个对象中指定的属性
        写法
//对象的数组在界显示是 需要(data.nk.name)
//直接使用name
const name = toRef(person, "name");
* 作用
    * 将某个响应式对象的某一个属性提供给外部使用
  • toRefs
    • 定义
      • 批量创建多个 ref 对象,其 value 值指向另一个对象中指定的属性
        写法
setup() {
let person = reactive({
name: "张三",
age: 19,
});
return {
...toRefs(person),
};
}
* 作用
    * 将某个响应式对象的全部属性提供给外部使用

给src属性赋值是图片无法显示

图片位置

image

赋值时

image

image

图表init错误

import * as echarts from 'echarts' //这样引入即可

el-input无法输入问题

添加属性 auto-complete="off"

使用mavonEditor编辑器

下载依赖

npm install mavon-editor --save

main.js引入

import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
Vue.use(mavonEditor)

使用组件

<mavon-editor
v-model="article.article_content_md"
style="height: 100%;"
ref=md
@save="saveArticles"
@imgAdd="$imgAdd"
fontSize="16px">
<button type="button" class="op-icon el-icon-document" :title="'摘要/封面'" slot="left-toolbar-after"
@click="dialogVisible = true"></button>
</mavon-editor>

上传图片

$imgAdd(pos, $file){
// 第一步.将图片上传到服务器.
var formdata = new FormData();
formdata.append('headimg', $file);
this.$axios({
url: 'http://127.0.0.1:3000/api/article/img/upimg',
method: 'post',
data: formdata,
headers: { 'Content-Type': 'multipart/form-data' },
}).then((res)=>{
console.log(res);
this.$refs.md.$img2Url(pos, res.data.data);
})
},

vue组件导入css

image

@import "../assets/css/cropper.css";

全局过滤

main.js

//过滤时间
Vue.filter("create_time", function(value) {
return moment(value).format("yyyy-MM-DD HH:mm:ss");
});

使用

<template v-slot="scope">
{{scope.row.create_time | create_time}}
</template>

Vue项目打包

项目下运行 生成dist文件

npm run build

上传到服务器

scp dist.zip root@IP:/存放地址

下载启动依赖

npm i -g serve

找到dist的路径 执行

serve -s dist

nginx打包

一样的先build 上传dist文件夹

https://www.runoob.com/linux/nginx-install-setup.html

修改nginx配置文件即可

创建vueTS(typescript)项目

命令

iShot_2022-07-09_11.08.03

iShot_2022-07-09_11.09.11

ui

image

image

image

解决element ui aside高度跟不上其他地方的

//设置样式高为height就行

data(){
return{
menuHeight: {
height: ""
}
}
},
created() {
//动态调整左侧菜单栏高度 document.documentElement.clientHeight
var docHeight = document.body.scrollHeight;
this.menuHeight.height = docHeight - 20 + "px";
console.log(this.menuHeight.height, "this.containerHeight.height");
}

样式穿透

>>>

.autose >>> input{
position: absolute;
}

/deep/

.wrapper /deep/ .swiper-pagination-bullet-active{
background: #fff;
}

slot获取序号

<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.$index+1 }}</span>
</template>

vue3+vite+ts无法识别App.vue

修改env.d.ts文件

declare module "*.vue" {
import { DefineComponent } from "vue"
const component: DefineComponent<{}, {}, any>
export default component
}

main.ts

import App from './App.vue';