使用cmd输入以下命令
npm install -g @vue/cli
# 验证是否安装成功
vue --version
创建一个项目
xxxxxxxxxx
#项目名称my-project不能出现大写但是可以用“-”进行连接
vue create my-project
xxxxxxxxxx
vue create vue-demo
项目名称不要出现大写
cd到项目里,然后使用
xxxxxxxxxx
npm run serve
v-html是原始html
x<template>
<h3>模板语法</h3>
<p>直接赋值:</p>
<p>数字运算:</p>
<p>三目运算符:</p>
<p>调用函数:</p>
<!-- 注意花括号内不支持换行表达式,只支持单行表达式 -->
<!-- <p></p> -->
<!-- 以上if就是本身需要换行的,即使不写else也是不支持的 -->
<p></p>
<p v-html = "rawHtml"></p>
</template>
<script>
export default{
data(){
return{
msg : "神奇的语法",
hello : "hello world",
number : 10,
ok : true,
message: "大家好",
rawHtml: "<a href = 'https://www.xiaoyangtongzhi.cn'>软件编程学习Y</a>"
}
}
}
</script>
xxxxxxxxxx
<template>
<!-- 由于v-bind非常常用所以v-bind可以省略直接在前面加: -->
<div v-bind:id="dynamicId" :class="dynamicClass" :title="dynamicTitle">测试</div>
<button :disabled="isButtonDisabled">Button</button>
<!-- 动态绑定多个值 -->
<div v-bind="objectOfAttrs">测试</div>
</template>
<script>
export default{
data(){
return{
dynamicClass:"appClass",
dynamicId:"appid",
dynamicTitle: undefined,//属性值为null/undefined值会被移除
isButtonDisabled: true,
objectOfAttrs: {//动态绑定多个值:一次性绑定多个属性
class: "appClass",
id: "appID",
}
}
}
}
</script>
<style>
.appClass{
color:red;
font-size:30px;
}
</style>
xxxxxxxxxx
<template>
<h3>条件渲染</h3>
<!-- 如果flag为真则"你能看见我吗"显示,如果为假则下面"那你还是看看我吧"显示 -->
<!-- 相当于if else语句 -->
<div v-if="flag">你能看见我吗</div>
<div v-else>那你还是看看我吧</div>
</template>
<script>
export default{
data(){
return{
flag:false,
}
}
}
</script>
xxxxxxxxxx
<template>
<h3>条件渲染</h3>
<!-- 下面相当于if else-if else语句 -->
<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>
</template>
<script>
export default{
data(){
return{
type:"D" //显示Not A/B/C
}
}
}
</script>
xxxxxxxxxx
<template>
<h3>条件渲染</h3>
<!-- flag为真则能看见,为假则看不见 -->
<!-- v-show是基于CSS的display: none;实现的 -->
<div v-show="flag">你能看见我吗</div>
</template>
<script>
export default{
data(){
return{
flag:false,
}
}
}
</script>
作用和python的for i in list1一样
以下for xxx in xxx中的in可以替换成of效果一样
xxxxxxxxxx
<template>
<h3>列表渲染</h3>
<!-- index是索引的下标 -->
<p v-for="(item,index) in names"> -</p>
</template>
<script>
export default{
data(){
return{
names:["小杨", "小李", "小琪"]
}
}
}
</script>
执行结果:
在网页源代码中生成三个便利的p标签
v-for解析json数据
xxxxxxxxxx
<template>
<h3>列表渲染</h3>
<!-- index是索引的下标 -->
<div v-for="(item,index) in result">
<p> -</p>
<img v-bind="objectOfAttrs" :src="item.imgLink" :alt="item.id"></img>
</div>
</template>
<script>
export default{
data(){
return{
objectOfAttrs:{
class: "imgClass",
id: "imgID"
},
result:[//模拟json格式数据
{
"id":1001,
"title":"西红柿鸡蛋打卤面",
"imgLink": "https://xiaoyangtongzhi.cn/link/food/img_food/xihongshijidandalumian.png"
},
{
"id":1002,
"title":"黄花菜木耳打卤面",
"imgLink": "https://xiaoyangtongzhi.cn/link/food/img_food/huanghuacaimuerdalumian.png"
},
{
"id":1003,
"title":"茄汁大虾",
"imgLink": "https://xiaoyangtongzhi.cn/link/food/img_food/qiezhidaxia.png"
}
]
}
}
}
</script>
<style>
.imgClass{
width:200px;
height: 150px;
}
</style>
运行结果:
v-for便利对象的所有属性
遍历对象有三个值,value key index 顺序不能改
xxxxxxxxxx
<template>
<h3>列表渲染</h3>
<div>
<!-- 使用v-for遍历对象,value是值,key是键名,index是下标 -->
<p v-for="(value,key,index) in userInfo"> - -</p>
</div>
</template>
<script>
export default{
data(){
return{
userInfo:{//对象
name:"小杨",
age:20,
sex:"男"
}
}
}
}
</script>
运行结果:
xxxxxxxxxx
<template>
<h3>key属性添加到v-for中</h3>
<p v-for="(item,index) in names" :key="index"></p>
</template>
<script>
export default {
data(){
return {
names:["小杨","小李","小张"],
}
}
}
</script>
注:一般不会把index付给key,key应该是不会变动的,如果往names列表中间加了新数据,那么所有的index就变动了
因此key一般赋值给id类不会变且唯一的值,如下例子:
xxxxxxxxxx
<template>
<h3>key属性添加到v-for中</h3>
<div v-for="item in result" :key="item.id">
<p></p>
<img v-bind="objectOfAttrs" :src="item.imgLink" :alt="item.id" />
</div>
</template>
<script>
export default {
data(){
return {
objectOfAttrs:{
class: "imgClass",
id: "imgID"
},
result:[//模拟json格式数据
{
"id":1001,
"title":"西红柿鸡蛋打卤面",
"imgLink": "https://xiaoyangtongzhi.cn/link/food/img_food/xihongshijidandalumian.png"
},
{
"id":1002,
"title":"黄花菜木耳打卤面",
"imgLink": "https://xiaoyangtongzhi.cn/link/food/img_food/huanghuacaimuerdalumian.png"
},
{
"id":1003,
"title":"茄汁大虾",
"imgLink": "https://xiaoyangtongzhi.cn/link/food/img_food/qiezhidaxia.png"
}
]
}
}
}
</script>
<style>
.imgClass{
width:200px;
height: 150px;
}
</style>
事件触发时执行的内联JavaScript语句(与onclick类似)
xxxxxxxxxx
<template>
<h3>内联事件处理器</h3>
<!-- v-on可以用@代替写成@click="count++" -->
<button v-on:click="count++">Add</button>
<p>Count is: </p>
</template>
<script>
export default{
data(){
return{
count:0
}
}
}
</script>
执行结果:
点击按钮不断累加
xxxxxxxxxx
<template>
<h3>内联事件处理器</h3>
<!-- click是单击 -->
<button @click="addCount">Add</button>
<br/>
<!-- dblclick是双击英文(double click缩写) -->
<button @dblclick="addCount">Add</button>
<p>Count is: </p>
</template>
<script>
export default{
data(){
return{
count:0
}
},
//以后所有的方法都放在这里
methods: {
addCount(){
console.log("点击了按钮");
//所有写在data里的值都可以用this.来访问
this.count++;
}
}
}
</script>
运行结果:
xxxxxxxxxx
<template>
<h3>内联事件处理器</h3>
<button @click="addCount">Add</button>
<p>Count is: </p>
</template>
<script>
export default{
data(){
return{
count:0
}
},
methods: {
addCount(e){
console.log(e);
console.log(e.target);
//vue中的event对象就是原生js的event对象
e.target.innerHTML = "Add" + this.count;
this.count++;
}
}
}
</script>
运行结果:
xxxxxxxxxx
<template>
<h3>内联事件处理器</h3>
<button @click="addCount('hello')">Add</button>
<p>Count is: </p>
</template>
<script>
export default{
data(){
return{
count:0
}
},
methods: {
addCount(msg){
console.log(msg);
this.count++;
}
}
}
</script>
运行结果:将hello传递过去并在控制台输出
xxxxxxxxxx
<template>
<h3>事件传参</h3>
<p @click="getNameHandler(name,$event)" v-for="(name,index) in names" :key="index"></p>
</template>
<script>
export default{
data(){
return{
names:["小杨","小李","小琪"]
}
},
methods:{
getNameHandler(name,event){
console.log(name);
console.log(event);
}
}
}
</script>
效果:当点击网页上生成的p标签时,点击哪个就会把哪个的名字和event对象在控制台输出
运行结果:
在处理事件时调用 event.preventDefault() 或 event.stopPropagation() 是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。
为解决这一问题,Vue 为 v-on 提供了事件修饰符。修饰符是用 .
表示的指令后缀,包含以下这些:
xxxxxxxxxx
<template>
<h3>事件修饰符-阻止默认事件</h3>
<!-- @click.prevent="clickHandle"和方法里的event.preventDefault();效果等价,阻止a标签点击跳转 -->
<a @click.prevent="clickHandle" href="https://www.xiaoyangtongzhi.cn">软件编程学习Y</a>"
</template>
<script>
export default{
data(){
return{
}
},
methods:{
clickHandle(event){
//通过event对象阻止默认事件
// event.preventDefault();
console.log("点击了事件");
}
}
}
</script>
运行结果:点击软件编程学习Y超链接不跳转
什么是事件冒泡:如下例子,p在div里,p和div都有个点击函数,如果我点击p,那么会同时触发div的点击函数和p的点击函数!
xxxxxxxxxx
<template>
<a @click.prevent="clickHandle" href="https://www.xiaoyangtongzhi.cn">软件编程学习Y</a>"
<h3>事件修饰符-阻止事件冒泡</h3>
<div @click="clickDiv">
<p @click="clickP">测试冒泡</p>
</div>
</template>
<script>
export default{
data(){
return{
}
},
methods:{
clickDiv(){
console.log("点击了div");
},
clickP(){
console.log("点击了p");
}
}
}
</script>
运行结果:
阻止事件冒泡1
xxxxxxxxxx
<h3>事件修饰符-阻止事件冒泡</h3>
<div @click="clickDiv">
<!-- @click.stop="clickP"和方法里的e.stopPropagation();效果等价,阻止事件冒泡 -->
<p @click.stop="clickP">测试冒泡</p>
</div>
或者
阻止事件冒泡2
xxxxxxxxxx
clickP(e){
e.stopPropagation();
console.log("点击了p");
}
用事件参数e调用stopPropagation()函数即可
运行结果:
vue能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些方法包括:
使用以下方法可以使原数组发生变化
练习例子:
xxxxxxxxxx
<template>
<h3>数组变化侦测</h3>
<button @click="pushListHandle">从后面添加数据</button>
<button @click="popListHandle">从后面删除数据</button>
<button @click="shiftListHandle">从前面删除数据</button>
<button @click="unshiftListHandle">从前面添加数据</button>
<button @click="spliceListHandle">替换删除数据</button>
<button @click="sortListHandle">排序数据</button>
<button @click="reverseListHandle">反转数据</button>
<ul>
<li v-for="(name,index) in names" :key="index"></li>
</ul>
</template>
<script>
export default{
data(){
return{
names:['张三','李四','王五','赵六']
}
},
methods:{
pushListHandle(){
this.names.push('小琪');
},
popListHandle(){
this.names.pop();
},
shiftListHandle(){
this.names.shift();
},
unshiftListHandle(){
this.names.unshift('小琪');
},
spliceListHandle(){
//把第一个位置替换为小琪,删除原列表从插入位置开始包含插入位置的两个数据
this.names.splice(1,2,'小琪');
},
sortListHandle(){
this.names.sort();
},
reverseListHandle(){
this.names.reverse();
}
}
}
</script>
变更方法,顾名思义,就是会对调用它们的原数组进行变更,相对地,也有一些不可变(immutable)方法,例如filter(),concat()和slice(),这些都不会更改原数组,而总是返回一个新数组,当遇到的是非变更方法时,我们需要将旧的数组替换为新的
使用以下三个方法不会使原数组发生变化,但会生成新数组保存结果
xxxxxxxxxx
<template>
<h3>数组变化侦测</h3>
<button @click="filterListHandle('赵六')">过滤数据</button>
<button @click="concatListHandle(['赵六','小琪'])">拼接列表</button>
<button @click="sliceListHandle(1, 3)">截取数组</button>
<ul>
<li v-for="(name,index) in names" :key="index"></li>
</ul>
</template>
<script>
export default{
data(){
return{
names:['张三','李四','王五']
}
},
methods:{
//不会引起UI自动更新,因为以下三个函数是生成新的数组
//要想让UI也跟着变化就重新赋值
filterListHandle(name){
this.names = this.names.filter(item=>item==name);
},
concatListHandle(list){
this.names = this.names.concat(list);
},
sliceListHandle(start,end){
this.names = this.names.slice(start, end);
}
}
}
</script>
xxxxxxxxxx
<template>
<h3>计算属性</h3>
<p></p>
<!-- 模板语法中的复杂逻辑建议使用第二种方法处理,使用computed计算属性 -->
<!-- 第一种方法不易处理复杂情况 -->
<!-- 第二种方法在数据没有变动的情况下,不管调用几次,都只会执行一次 -->
<!-- 第三种函数方法,每执行一次,就会调用一次,非常浪费内存,所以不建议使用 -->
<p>第一种直接写逻辑:</p>
<p>第二种计算属性:</p>
<p>第三种使用函数:</p>
</template>
<script>
export default {
data() {
return {
itxiaoyang:{
name:"小杨同志",
content:["前端","Java","python"]
}
}
},
//计算属性
computed:{
itxiaoyangContent(){
return this.itxiaoyang.content.length > 0 ? "yes":"no";
}
},
methods:{
itxiaoyangContents(){
return this.itxiaoyang.content.length > 0 ? "yes":"no";
}
}
}
</script>
区别:
运行结果:
computed计算属性不能够传递参数,但是可以使用闭包方式实现,下面是一个例子:
xxxxxxxxxx
<template>
<p></p>
</template>
<script>
export default{
data(){
return{
names:["张三","李四","王五","赵六","黄七"],
}
},
computed:{
//isContainName()这里不能接收参数,但是可以直接返回一个函数,并让这个函数接收参数,该方法称为闭包
isContainName(){
return function(name){
for(var i of this.names){
if(i == name){
return true;
}
}
return false;
}
}
}
}
</script>
xxxxxxxxxx
<template>
<!-- 对象作为class绑定 -->
<p :class="classObject">Class样式绑定1</p>
<!-- 直接绑定多个class -->
<p :class="{'active':isActive, 'text-danger':textDanger}">Class样式绑定2</p>
<!-- 数组作为class绑定 -->
<p :class="[arrActive, arrHasError]">Class样式绑定3</p>
<!-- 数组三元运算符作为class绑定 -->
<p :class="[isActive? 'active':'text-danger']">Class样式绑定4</p>
<!-- 数组和对象一起作为class绑定,注意:只能是数组嵌套对象 -->
<p :class="[{'active':isActive}, arrHasError]">Class样式绑定5</p>
</template>
<script>
export default {
data() {
return {
isActive: true,
textDanger: true,
classObject:{
'active':true,
'text-danger':true
},
arrActive: "active",
arrHasError:"text-danger"
}
}
}
</script>
<style>
.active {
font-size: 30px;
}
.text-danger {
color: red;
}
</style>
运行结果:
xxxxxxxxxx
<template>
<!-- 直接写法 -->
<p :style="{color:activeColor,fontSize:fontSize}">Style绑定1</p>
<!-- 绑定对象格式-常用写法 -->
<p :style="styleObject">Style绑定2</p>
</template>
<script>
export default {
data() {
return {
activeColor:'green',
fontSize:'20px',
styleObject:{
color:'red',
fontSize:'30px'
}
}
}
}
</script>
运行结果:
xxxxxxxxxx
<template>
<h3>侦听器</h3>
<p></p>
<button @click="updateHandle">修改数据</button>
</template>
<script>
export default{
data(){
return{
message:"hello"
}
},
methods:{
updateHandle(){
this.message="world"
}
},
watch:{
message(newValue,oldValue){
// 数据发生变化自动执行的函数
// 函数名必须与侦听的数据对象保持一致
console.log("新值:" + newValue, "旧值:" + oldValue);
}
}
}
</script>
运行结果:
xxxxxxxxxx
<template>
<h3>表单输入绑定</h3>
<form>
<!-- v-model有三个修饰符.lazy关闭输入一次搜索一次 .number只接受数字 .trim去除空格 -->
<input type="text" v-model.lazy="message" placeholder="请输入信息">
<p></p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox"></label>
</form>
</template>
<script>
export default{
data(){
return{
message:"",
checked:false
}
}
}
</script>
运行结果:
xxxxxxxxxx
<template>
<div ref="container" class="container"></div>
<input type="text" ref="username"/>
<button @click="getElementHandle">获取元素</button>
</template>
<script>
/**
* 内容改变:{{ 模板语法 }}
* 属性改变:v-bind:属性名="属性值" 或者省略只写 :属性名="属性值"
* 事件绑定:v-on:事件名="方法名" 或者省略只写 @事件名="方法名"
*
* 操作DOM:this.$refs.元素ref名.元素属性名 = "属性值";
* 注意:ref很消耗性能,没有必要不要操作DOM
*/
export default{
data(){
return{
content:"hello world!"
}
},
methods:{
getElementHandle(){
console.log(this.$refs.container);
this.$refs.container.innerHTML = "你好世界!";
console.log(this.$refs.username.value);
}
}
}
</script>
运行结果:
组件最大的优势就是可复用性
当使用构建步骤时,我们一般会将Vue组件定义在一个单独的.vue文件中,这被叫做单文件组成(简称SFC)
App.vue
xxxxxxxxxx
<template>
<!-- 第三步 显示组件 -->
<MyComponent/>
</template>
<script>
// 第一步 引入组件
import MyComponent from './components/MyComponent.vue'
export default{
//第二步 注入组件
components: {
MyComponent
}
}
</script>
<style>
</style>
MyComponent.vue
xxxxxxxxxx
<template>
<div class="container"></div>
</template>
<script>
export default {
data() {
return {
message: '组件基础组成'
}
}
}
</script>
<!-- scoped 让当前样式只在当前组件中生效-->
<style scoped>
.container {
font-size: 30px;
color: red;
}
</style>
全局注册
在main.js中进行注册
main.js 以下为注册一个Header.vue示例
xxxxxxxxxx
import { createApp } from 'vue'
import App from './App.vue'
import Header from './pages/Header.vue'
const app = createApp(App)
//在这之间写全局组件的注册
app.component('Header', Header)
app.mount('#app')
在App.vue中就不用在script里写import导入了,但是需要在template中写引用
局部注册
以下为局部注册示例,开发中推荐使用局部注册的方式
xxxxxxxxxx
<template>
<Header />
<Main />
<Aside />
</template>
<script setup>
import Header from './pages/Header.vue'
import Main from './pages/Main.vue'
import Aside from './pages/Aside.vue'
</script>
Parent.vue
xxxxxxxxxx
<template>
<h3>Parent</h3>
<Child title="Parent数据" :demo="message" :age="age", :names="names" :userInfo="userInfo" />
</template>
<script>
import Child from './Child.vue'
export default {
data() {
return {
message:'ParentHello',
age:18,
names:['张三','李四','王五'],
userInfo:{
name:'张三',
age:18,
sex:'男'
}
}
},
components: {
Child
}
}
</script>
Child.vue
xxxxxxxxxx
<template>
<h3>Child</h3>
<p></p>
<p></p>
<p></p>
<ul>
<li v-for="(item,index) in names" :key="index"></li>
</ul>
<p v-for="(item,index) in userInfo" :key="index"></p>
</template>
<script>
export default{
data(){
return{
}
},
props:["title","demo","age","names","userInfo"]
}
</script>
运行结果:
注意:props传递数据只能从父级传递给子级
ComponentA.vue
xxxxxxxxxx
<template>
<h3>ComponentA</h3>
<ComponentB :title="title" :names="names" />
</template>
<script>
import ComponentB from './ComponentB.vue'
export default{
data(){
return{
title:"hello world!",
names:['张三','李四','王五']
}
},
components:{
ComponentB
}
}
</script>
ComponentB.vue
xxxxxxxxxx
<template>
<h3>ComponentB</h3>
<p></p>
<p v-for="(name,index) in names" :key="index"></p>
</template>
<script>
export default{
data(){
return{
}
},
props:{
//props里是从父元素里传过来的数据是不允许被修改的
title:{
// 常用类型如下,可以接收多种类型,也可以接收一种
type:[String, Number, Array, Object, Boolean],
default:'默认值',
required:true//必须要传入
},
//数字和字符串可以直接default,但是如果是数组和对象,必须通过工厂函数返回默认值
names:{
type:Array,
default(){
return ['默认值1','默认值2','默认值3']
}
}
}
}
</script>
运行结果:
ComponentEvent.vue
xxxxxxxxxx
<template>
<h3>组件事件</h3>
<Child @someEvent="getHandle" />
<p>父元素:</p>
</template>
<script>
import Child from './Child.vue'
export default{
data(){
return{
message:""
}
},
components:{
Child
},
methods:{
getHandle(data){
console.log("触发了",data);
this.message = data;
}
}
}
</script>
Child.vue
xxxxxxxxxx
<template>
<h3>Child</h3>
<!-- 调用clickEventHandle函数 -->
<button @click="clickEventHandle">传递数据</button>
</template>
<script>
export default{
data(){
return{
msg:"child数据"
}
},
methods:{
clickEventHandle(){
//传递数据,第一个参数名字,第二个是传递的数据
this.$emit('someEvent',this.msg)
}
}
}
</script>
运行结果:
效果:使用watch侦听器可以使得父界面和子界面输入同步
SearchComponent.vue
xxxxxxxxxx
<template>
<h3>Search Component</h3>
<label>搜索:</label><input type="text" v-model="search">
<p>SearchComponent的数据:</p>
</template>
<script>
export default{
data(){
return{
search:''
}
},
//侦听器
watch:{
search(newVal, oldVal){
console.log("新值:"+newVal,"旧值:"+oldVal);
this.$emit('sendInfo',this.search);
}
}
}
</script>
Main.vue
xxxxxxxxxx
<template>
<h3>Main</h3>
<p>Main的数据:</p>
<SearchComponent @sendInfo="receiveInfo" />
</template>
<script>
import SearchComponent from './SearchComponent.vue'
export default{
data(){
return{
data:""
}
},
components:{
SearchComponent
},
methods:{
receiveInfo(data){
console.log(data)
this.data=data
}
}
}
</script>
运行结果:
本质是父给子传递一个函数,函数里有参数,子给参数赋值返回给父
ComponentA.vue
xxxxxxxxxx
<template>
<h3>ComponentA</h3>
<p>父元素:</p>
<ComponentB title="标题" :onEvent="dataFn"/>
</template>
<script>
import ComponentB from './ComponentB.vue'
export default {
data() {
return {
message:""
}
},
components:{
ComponentB
},
methods:{
dataFn(data){
console.log(data);
this.message = data;
}
}
}</script>
ComponentB.vue
xxxxxxxxxx
<template>
<h3>ComponentB</h3>
<p></p>
</template>
<script>
export default {
data() {
return {
}
},
props:{
title:String,
onEvent:Function
}
}</script>
运行结果:
App.vue
xxxxxxxxxx
<template>
<SlotsBase>
<!-- 将整个div传递给SlotsBase子组件 -->
<div>
<h3>插槽标题</h3>
<p>插槽内容</p>
</div>
</SlotsBase>
</template>
<script>
import SlotsBase from './components/SlotsBase.vue'
export default{
components:{
SlotsBase
}
}
</script>
<style>
</style>
SlotsBase.vue
xxxxxxxxxx
<template>
<h3>插槽基础知识</h3>
<slot>
<!-- 插槽传递过来的div将会在这里显示 -->
</slot>
</template>
<script>
</script>
运行结果:
xxxxxxxxxx
<template>
<h3>SlotsTwo</h3>
<slot>插槽默认值</slot>
</template>
如果没有东西传过来那么slot里就会显示默认值
使用v-slot进行命名 v-slot:可以写成#号
传递slot
xxxxxxxxxx
<SlotsTwo>
<!-- v-slot:可以写成#号-->
<template v-slot:header>
<h3></h3>
</template>
<template #main>
<p>内容</p>
</template>
</SlotsTwo>
接收slot
使用name = ""对多个slot指定接收的名称
xxxxxxxxxx
<template>
<h3>SlotsTwo</h3>
<slot name = "header">插槽默认值</slot>
<hr>
<slot name="main"></slot>
</template>
App.vue
xxxxxxxxxx
<template>
<SlotsAttr>
<template #header="slotProps">
<h3> -</h3>
</template>
<template #main="slotProps">
<h3></h3>
</template>
</SlotsAttr>
</template>
<script>
import SlotsAttr from './components/SlotsAttr.vue'
export default{
data(){
return{
currentTest:"测试内容"
}
},
components:{
SlotsAttr
}
}
</script>
SlotsAttr.vue
xxxxxxxxxx
<template>
<h3>SlotsAttr</h3>
<hr>
<slot name="header" :header="childMessage"></slot>
<slot name="main" :job="jobMessage"></slot>
</template>
<script>
export default {
data(){
return{
childMessage:"子组件数据",
jobMessage:"xiaoyangtongzhi"
}
}
}
</script>
运行结果:
xxxxxxxxxx
<template>
<h3>组件的生命周期</h3>
<p></p>
<button @click="updateHandle">更新</button>
</template>
<script>
/** 八个生命周期
* 1.创建期:beforeCreate created
* 2.挂载期:beforeMount mounted
* 3.更新期:beforeUpdate updated
* 4.销毁期:beforeUnmount unmounted
*/
export default{
data(){
return{
msg:'更新之前'
}
},
methods:{
updateHandle(){
this.msg = '更新之后';
}
},
beforeCreate(){
console.log('组件创建之前');
},
created(){
console.log('组件创建之后');
},
beforeMount(){
console.log('组件挂载之前');
},
mounted(){
console.log('组件挂载之后');
},
beforeUpdate(){
console.log('组件更新之前');
},
updated(){
console.log('组件更新之后');
},
beforeUnmount(){
console.log('组件销毁之前');
},
unmounted(){
console.log('组件销毁之后');
}
}
</script>
xxxxxxxxxx
<template>
<h3>组件生命周期的函数应用</h3>
<p ref="name">小杨同志</p>
<ul>
<li v-for="(item,index) in banner" :key="index">
<h3></h3>
<p></p>
</li>
</ul>
</template>
<script>
export default{
data(){
return{
banner:[]
}
},
beforeMount(){
console.log(this.$refs.name);//undefined
},
//组件挂在之后
mounted(){
console.log(this.$refs.name);//可以读取到name这个组件
//模拟网络请求
this.banner=[
{
title:"标题1",
content:"内容1"
},
{
title:"标题2",
content:"内容2"
},
{
title:"标题3",
content:"内容3"
}
]
},
}
</script>
ComponentA.vue
xxxxxxxxxx
<template>
<h3>ComponentA</h3>
</template>
ComponentB.vue
xxxxxxxxxx
<template>
<h3>ComponentB</h3>
</template>
App.vue
xxxxxxxxxx
<template>
<!-- <UserComponent /> -->
<component :is="tabComponent"></component>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import ComponentA from './components/ComponentA.vue'
import ComponentB from './components/ComponentB.vue'
export default{
data(){
return{
tabComponent:"ComponentA"
}
},
components:{
ComponentA,
ComponentB
},
methods:{
changeHandle(){
if(this.tabComponent == "ComponentA"){
this.tabComponent = "ComponentB"
}else{
this.tabComponent = "ComponentA"
}
}
}
}
</script>
通过点击切换组件按钮进行切换
ComponentA.vue
xxxxxxxxxx
<template>
<h3>ComponentA</h3>
<p></p>
<button @click="updateHandle">更新数据</button>
</template>
<script>
export default{
data(){
return{
msg:"老数据"
}
},
beforeUnmount(){
console.log("组件被卸载")
},
unmount(){
console.log("组件被卸载")
},
methods:{
updateHandle(){
this.msg = "新数据"
}
}
}
</script>
ComponentB.vue
xxxxxxxxxx
<template>
<h3>ComponentB</h3>
</template>
App.vue
xxxxxxxxxx
<template>
<!-- keep-alive用于保持组件存活,当组件被切换时,不会销毁,而是缓存起来,当组件重新被显示时,会重新显示,不会重新创建 -->
<keep-alive>
<component :is="tabComponent"></component>
</keep-alive>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import ComponentA from './components/ComponentA.vue'
import ComponentB from './components/ComponentB.vue'
export default{
data(){
return{
tabComponent:"ComponentA"
}
},
components:{
ComponentA,
ComponentB
},
methods:{
changeHandle(){
if(this.tabComponent == "ComponentA"){
this.tabComponent = "ComponentB"
}else{
this.tabComponent = "ComponentA"
}
}
}
}
</script>
运行效果:
当点击更新数据的时候,A的老数据会变成新数据,点击切换组件,正常组件会被销毁,但是加了<keep-alive>
进行包裹后,点击切换回去同样还是新数据
<keep-alive>
用于保持组件存活,当组件被切换时,不会销毁,而是缓存起来,当组件重新被显示时,会重新显示,不会重新创建。
ComponentA.vue
xxxxxxxxxx
<template>
<h3>ComponentA</h3>
<p></p>
<button @click="updateHandle">更新数据</button>
</template>
<script>
export default{
data(){
return{
msg:"老数据"
}
},
beforeUnmount(){
console.log("组件被卸载")
},
unmount(){
console.log("组件被卸载")
},
methods:{
updateHandle(){
this.msg = "新数据"
}
}
}
</script>
ComponentB.vue
xxxxxxxxxx
<template>
<h3>ComponentB</h3>
</template>
App.vue
xxxxxxxxxx
<template>
<!-- keep-alive用于保持组件存活,当组件被切换时,不会销毁,而是缓存起来,当组件重新被显示时,会重新显示,不会重新创建 -->
<keep-alive>
<component :is="tabComponent"></component>
</keep-alive>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import ComponentA from './components/ComponentA.vue'
// import ComponentB from './components/ComponentB.vue'
import {defineAsyncComponent} from 'vue'
//异步加载组件
const ComponentB = defineAsyncComponent(() =>
import('./components/ComponentB.vue')
)
export default{
data(){
return{
tabComponent:"ComponentA"
}
},
components:{
ComponentA,
ComponentB
},
methods:{
changeHandle(){
if(this.tabComponent == "ComponentA"){
this.tabComponent = "ComponentB"
}else{
this.tabComponent = "ComponentA"
}
}
}
}
</script>
运行效果:
当进入页面时会进行一次组件和数据的加载但是ComponentB是异步加载,所以ComponentB是不会被加载进来的,当点击切换组件后,ComponentB.vue会在网络请求中进行加载。
注意:provide和inject只能由上往下传(即祖宗给子孙传)
main.js
xxxxxxxxxx
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 全局数据,在任何地方都能读取该数据
app.provide("globalData","我是全局数据,在任何地方不管有没有关系都可以读取")
app.mount('#app')
注意:使用provide创建的globalData为全局数据,在任何地方不管有没有关系都可以读取。
App.vue
xxxxxxxxxx
<template>
<h3>祖宗</h3>
<Parent/>
</template>
<script>
import Parent from './components/Parent.vue'
export default{
data(){
return{
msg:'爷爷的财产~~~'
}
},
components:{
Parent
},
provide(){
return{
msg:this.msg
}
}
}
</script>
Parent.vue
xxxxxxxxxx
<template>
<h3>Parent</h3>
<p></p>
<Child />
</template>
<script>
import Child from './Child.vue'
export default{
inject:['globalData'],
components:{
Child
}
}
</script>
Child.vue
xxxxxxxxxx
<template>
<h3>Child</h3>
<p></p>
<p></p>
<p></p>
</template>
<script>
export default{
inject:['msg','globalData'],
// 也可以将msg先传递给data进行处理再显示
data(){
return{
fullMsg:this.msg+String("123")
}
}
}
</script>
运行结果:
Swiper开源 免费 强大 自适应的触摸滑动插件
xxxxxxxxxx
1.安装最新swiper
npm i swiper --save
安装指定版本的swiper
npm i swiper@8.1.6 --save
xxxxxxxxxx
<template>
<div class="hello">
<swiper :modules="modules" :pagination="{ clickable: true }">
<SwiperSlide>
<img src=" https://xiaoyangtongzhi.cn/link/food/img_food/xihongshijidandalumian.png" alt="vue logo">
</SwiperSlide>
<SwiperSlide>
<img src="https://xiaoyangtongzhi.cn/link/food/img_food/qiedingroumodalumian.png" alt="vue logo">
</SwiperSlide>
<SwiperSlide>
<img src="https://xiaoyangtongzhi.cn/link/food/img_food/moguroumodalumian.png" alt="vue logo">
</SwiperSlide>
</swiper>
</div>
</template>
<script>
import { Pagination } from 'swiper';
import 'swiper/css/pagination';
import { Swiper, SwiperSlide } from 'swiper/vue';
import 'swiper/css';
export default {
data(){
return{
modules: [Pagination],
}
},
components: {
Swiper,
SwiperSlide,
Pagination,
},
}
</script>
<style scoped>
img{
width: 100%;
}
</style>
运行结果:
xxxxxxxxxx
Axios需要安装并不是vue内部集成的
npm install --save axios
字符串的转换安装包
npm install --save querystring
在main.js进行全局引用,日后开发需要很多用到网络请求的地方,因此进行全局引用
前提:已进行跨域
xxxxxxxxxx
import axios from 'axios'
const app = createApp(App)
app.config.globalProperties.$axios = axios
app.mount('#app')
xxxxxxxxxx
<template>
<div class="hello">
<p></p>
</div>
</template>
<script>
// import axios from 'axios';
import querystring from 'querystring';
export default {
data() {
return {
msg: ''
}
},
mounted() {
//get请求方式
this.$axios({
method:"get",
url:"http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php",
}).then(res=>{
console.log(res.data);
this.msg = res.data.chengpinDetails[0];
})
//post请求方式
this.$axios({
method:"post",
url:"http://iwenwiki.com/api/blueberrypai/login.php",
data:querystring.stringify({
user_id: "iwen@qq.com",
password: "iwen123",
verification_code:"crfvw"
})
}).then(res=>{
console.log(res.data);
})
//快捷方案get
this.$axios.get("http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php").then(res=>{
console.log(res.data);
})
//快捷方式post
this.$axios.post("http://iwenwiki.com/api/blueberrypai/login.php",querystring.stringify({
user_id: "iwen@qq.com",
password: "iwen123",
verification_code:"crfvw"
})).then(res=>{
console.log(res.data);
})
}
}
</script>
运行结果:
1.在src下新建utils文件夹
1.1创建request.js
request.js
xxxxxxxxxx
import axios from "axios";
import querystring from "querystring";
//参考文档 https://www.kancloud.cn/yunye/axios/234845
const errorHandle = (status,info) => {
switch (status) {
case 400:
console.log("语义有误");
break;
case 401:
console.log("服务器认证失败");
break;
case 403:
console.log("服务器拒绝访问");
break;
case 404:
console.log("请求错误,未找到该资源");
break;
case 405:
console.log("请求方法未允许");
break;
case 500:
console.log("服务器内部错误");
break;
case 501:
console.log("服务器不支持该请求的函数");
break;
case 502:
console.log("服务器无响应");
break;
default:
console.log(info);
break;
}
}
const instance = axios.create({
// 网络请求的公共配置
timeout: 5000,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
}
});
// 请求拦截器最常用的
// 发送数据之前
instance.interceptors.request.use(
config => {
if(config.method === "post"){
config.data = querystring.stringify(config.data);
}
// console.log(config);
// config包含网络请求的所有信息
return config;
},
error => {
// console.log(error);
return Promise.reject(error);
}
)
// 响应拦截器
instance.interceptors.response.use(
response => {
// console.log(response);
return response.status === 200 ? Promise.resolve(response) : Promise.reject(response);
},
error => {
// console.log(error);
const { response } = error;
errorHandle(response.status,response.info);
}
)
export default instance;
2.在src下新建api文件夹
2.1在api文件夹下创建index.js和path.js
path.js
xxxxxxxxxx
const base = {
baseUrl:"http://localhost:5001/",
peopleAdmin:"api/peopleAdmin",
delPeopleAdmin:"api/delPeopleAdmin"
}
export default base;
index.js
xxxxxxxxxx
import axios from "../utils/request";
import path from "./path";
const api = {
//请求管理人员表格数据
getPeopleAdminTableData(){
return axios.get(path.baseUrl+path.peopleAdmin);
},
//删除管理人员数据
postDelPeopleAdmin(idList){
return axios.post(path.baseUrl+path.delPeopleAdmin,{ idList: JSON.stringify(idList) });
}
}
export default api;
3.在components文件夹下试用vue进行引用
xxxxxxxxxx
<template>
</template>
<script setup>
import api from '../api/index';
//GET
api.getPeopleAdminTableData().then(res=>{
// 清空原有数据再填充新数据
tableData.value = [];
// console.log(res.data)
for(var i in res.data){
tableData.value.push(res.data[i])
}
//给分页数据总长度赋值
total.value = parseInt(tableData.value.length)
})
//POST
api.postDelPeopleAdmin(idList).then(res=>{
// console.log("message",res.data.message)
// console.log("status",res.status)
if(res.status == 200 && res.data.message == "删除成功"){
ElMessage.success('删除成功')
//重新请求表格数据
getTableData()
}
if(res.data.message == "删除失败"){
ElMessage.error('删除失败')
}
})
</script>
后端python
app.py
xxxxxxxxxx
from flask import Flask
from views.peopleAdmin import getPeopleAdminTableData,delPeopleAdminTableData
from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})
# 获取peopleAdmin数据
app.add_url_rule('/api/peopleAdmin', methods=['GET'], view_func = getPeopleAdminTableData)
# 删除peopleAdmin数据
app.add_url_rule('/api/delPeopleAdmin', methods=['POST'] ,view_func = delPeopleAdminTableData)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5001)
views/peopleAdmin.py
xxxxxxxxxx
from flask import jsonify,request
import json
from conn_sql import conn
def getPeopleAdminTableData():
cur = conn.cursor()
select_sql = "SELECT * FROM admin_user"
cur.execute(select_sql)
result = cur.fetchall()
# 将查询结果转换为字典列表
data_list = [{column[0]: value for column, value in zip(cur.description, row)} for row in result]
return jsonify(data_list)
def delPeopleAdminTableData():
# 获取请求中的idList参数
id_list = request.form.get('idList') # 假设前端传递的是以某种方式分隔的字符串,如"1,2,3"
id_list = json.loads(id_list) #[1,2,3]
# return jsonify({"return message": id_list})
# 构造用于 IN 子句的占位符字符串
ids = ','.join(['%s'] * len(id_list))
# 执行SQL删除操作
try:
delete_sql = f'DELETE FROM admin_user WHERE id IN ({ids})'
cur = conn.cursor()
cur.execute(delete_sql, id_list)
conn.commit() # 提交事务
return jsonify({"message": "删除成功"}), 200
except Exception as e:
conn.rollback() # 如果发生错误,回滚事务
print(f"删除操作时出现错误: {e}")
return jsonify({"message": "删除失败"}), 500
finally:
cur.close() # 关闭游标
解决方法:
1.后台解决
2.前台解决
这里只讲前台解决
在vue.config.js中的transpileDependencies: true,后面加入,url需要放入产生跨域的域名即可
xxxxxxxxxx
devServer:{
proxy: {
'/api': {
target: 'https://www.xiaoyangtongzhi.cn/', //接口域名
changeOrigin: true, //是否跨域
ws: true, //是否代理 websockets
secure: true, //是否https接口
pathRewrite: { //路径重置
'^/api': ''
}
}
}
}
在vue文件中写请求 在请求的时候无需再写域名,但是需要加入vue.config.js配置的代理名/api
例如 axios.get('/api/剩余域名')
重启服务器
xxxxxxxxxx
<template>
<h3>Axios跨域解决方案</h3>
</template>
<script>
import axios from 'axios';
export default {
mounted(){
axios.get('/api/link/food/php/auto_search.php')
.then((res) => {
console.log(res.data);
})
}
}
</script>
运行结果:
xxxxxxxxxx
1.安装路由命令
npm install --save vue-router
在src下创建router文件夹
2.1 router下创建index.js
index.js
xxxxxxxxxx
import { createRouter, createWebHashHistory } from "vue-router"
import HomeView from '../views/HomeView'
//配置信息中需要页面的相关配置
const routes = [
{
path:"/",
name: 'home',
component:HomeView
},
{
path:"/about",
name: 'about',
//这是异步加载方式
component: () => import('../views/AboutView.vue')
}
]
//创建路由
const router = createRouter({
/**
* createWebHashHistory
* home:http://localhost:8080/#/
* about:http://localhost:8080/#/about
* 此种方式不会出现404问题
* 原理:a标签锚点链接
*
* createWebHistory
* home:http://localhost:8080/
* about:http://localhost:8080/about
* 此种方式需要后台配合做重定向,否则会出现404问题
* 原理:H5的pushState()方法
*/
history: createWebHashHistory(),
routes
})
export default router;
src下创建views文件夹,用于放页面
3.1 views文件夹内创建HomeView.vue和AboutView.vue
HomeView.vue
xxxxxxxxxx
<template>
<h3>首页</h3>
</template>
AboutView.vue
xxxxxxxxxx
<template>
<h3>关于页面</h3>
</template>
在main.js引入router下的index.js
main.js
xxxxxxxxxx
import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router/index'
createApp(App).use(router).mount('#app')
App.vue
xxxxxxxxxx
<template>
<!-- 路由的显示入口 -->
<router-link to="/">首页</router-link>|
<router-link to="/about">关于</router-link>
<router-view></router-view>
</template>
<style>
#app {
text-align: center;
margin-top: 60px;
}
</style>
运行结果:
点击首页
点击关于
router下index.js
xxxxxxxxxx
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue')
},
{
path:'/news',
name:'news',
//异步加载方式
component:() => import('../views/NewsView.vue')
},
{
path:'/newsdetails/:name',
name:'newsdetails',
component:() => import('../views/NewsDetailsView.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
views文件夹下
HomeView.vue
xxxxxxxxxx
<template>
<h3>首页</h3>
</template>
AboutView.vue
xxxxxxxxxx
<template>
<h3>关于页面</h3>
</template>
NewsView.vue
xxxxxxxxxx
<template>
<h3>新闻</h3>
<ul>
<li><router-link to="/newsdetails/百度">百度新闻</router-link></li>
<li><router-link to="/newsdetails/网易">网易新闻</router-link></li>
<li><router-link to="/newsdetails/头条">头条新闻</router-link></li>
</ul>
</template>
<style scoped>
li {
list-style: none;
}
</style>
NewsDetailsView.vue
xxxxxxxxxx
<template>
<h3>新闻详情</h3>
<!-- 接收参数,name是在index.js里设置的:name -->
<p></p>
</template>
Main.js
xxxxxxxxxx
import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
createApp(App).use(router).mount('#app')
App.vue
xxxxxxxxxx
<template>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/news">News</router-link>
</nav>
<router-view/>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
font-smoothing: antialiased;
osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
</style>
运行结果:
新闻界面
点击百度新闻
点击网易新闻
二级路由即是在index.js中加入children属性
router下index.js
xxxxxxxxxx
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
redirect: '/about/us',//默认进入关于我们的页面
component: () => import('../views/AboutView.vue'),
children: [
{
//注意 二级导航的path不要加/
path: 'us',
name: 'us',
component: () => import('../views/AboutSub/AboutUs.vue')
},
{
//注意 二级导航的path不要加/
path: 'info',
name: 'info',
component: () => import('../views/AboutSub/AboutInfo.vue')
}
]
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
在二级导航页面加
AboutView.vue
xxxxxxxxxx
<template>
<div class="about">
<router-link to="/about/us">关于我们</router-link> |
<router-link to="/about/info">关于信息</router-link>
<router-view></router-view>
</div>
</template>
实现效果:
默认进去显示关于我们
点击关于信息 显示关于信息
1.安装vuex
npm install vuex --save
2.在src下创建store文件夹
2.1 store下创建index.js
index.js
xxxxxxxxxx
import { createStore } from 'vuex'
// Vuex的核心就是帮我们管理组件之间的数据(状态)的
export default createStore({
//所有的数据(状态)都放在state里
state: {
counter: 0
}
})
3.main.js中引入index.js
main.js
xxxxxxxxxx
import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import store from './store/index'
createApp(App).use(store).mount('#app')
4.使用数据
App.vue 引入数据的第一种方式
xxxxxxxxxx
<template>
<!-- 引入数据的第一种方式 -->
<p>counter: </p>
<HelloWorld/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
components:{
HelloWorld
}
}
</script>
HelloWorld.vue 引入数据的第二种方式
xxxxxxxxxx
<template>
<!-- 3.直接读取值 -->
<p>counter: </p>
</template>
<script>
//1.引用数据的第二种方式
import { mapState } from 'vuex';
export default {
computed:{
//2.在这里专门读取vuex的数据
mapState(['counter'])
}
}
</script>
效果图:
最常用的核心有:State Getter Mutation Action
state是数据管理,vue状态管理中已讲
1.Getter 对Vuex中的数据进行过滤
2.Mutation数据读取和修改
3.Action功能类似于Mutation,区别是Action进行异步提交数据给Mutation,然后由Mutation进行修改,有异步要求的话Action要配合Mutationji那些修改,如果没有异步要求Mutation自己修改即可。
默认创建项目时选中router和vuex,且已配置好axios跨域
store文件夹下index.js
xxxxxxxxxx
import { createStore } from 'vuex'
import axios from 'axios'
export default createStore({
state: {
counter:0
},
//对数据进行过滤
getters:{
getCounter(state){
return state.counter>0 ? state.counter : "counter小于0,不符合要求"
}
},
//对数据进行读取和修改,任何组件引用这个值都会得到相应的修改
mutations:{
addCounter(state,num){
state.counter += num
}
},
//action只允许异步操作,不能直接修改数据,只能通过mutations修改数据
actions:{
asyncAddCounter({commit}){
axios.get('/api/link/food/php/auto_search.php').then(res=>{
console.log(res.data[0].no);
//res.data[0].no的值应该是3
//将res.data[0].no转为整数
commit('addCounter',parseInt(res.data[0].no))
})
}
}
})
views文件夹下HomeView.vue 第一种读取数据的方式
xxxxxxxxxx
<template>
<div class="home">
<h3>home</h3>
<p>Home-counter: </p>
<button @click="addClickHandler">增加</button>
<button @click="addAsyncClickHandler">异步增加</button>
</div>
</template>
<script>
export default {
methods: {
addClickHandler() {
//固定调用vuex的方法
this.$store.commit("addCounter",15);
},
addAsyncClickHandler() {
this.$store.dispatch("asyncAddCounter");
}
}
};
</script>
注意:vue3中不再使用this.$store.commit("addCounter",15);而是使用下面这种按需导入的方式,取值也是不能用$store.state,用store.state.xxx
xxxxxxxxxx
<script>
import { useStore } from 'vuex'
// 获取vuex中的store
const store = useStore()
export default {
methods: {
addClickHandler() {
//vue3调用vuex的方法
store.commit("addCounter",15);
},
addAsyncClickHandler() {
store.dispatch("asyncAddCounter");
}
}
};
</script>
views文件夹下AboutView.vue 第二种读取数据的方式
xxxxxxxxxx
<template>
<div class="about">
<h3>关于</h3>
<p>About-counter: </p>
<button @click="addClickHandler">增加</button>
<button @click="addAsyncClickHandler">异步增加</button>
</div>
</template>
<script>
import { mapGetters, mapMutations,mapActions } from "vuex";
export default {
computed:{
mapGetters(["getCounter"])
},
methods:{
mapMutations(["addCounter"]),
mapActions(["asyncAddCounter"]),
addClickHandler() {
//固定调用vuex的方法
this.addCounter(15);
},
addAsyncClickHandler() {
this.asyncAddCounter();
}
}
}
</script>
main.js 引用路由router和vuex
xxxxxxxxxx
import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
createApp(App).use(store).use(router).mount('#app')
vue.config.js 配置跨域使用axios
xxxxxxxxxx
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer:{
proxy: {
'/api': {
target: 'https://xiaoyangtongzhi.cn/', //接口域名
changeOrigin: true, //是否跨域
ws: true, //是否代理 websockets
secure: true, //是否https接口
pathRewrite: { //路径重置
'^/api': ''
}
}
}
}
})
运行效果:
只展示第一种home界面下,因为两种读取数据的方式不同,但是效果是一样的
这是由getters里getCounter函数对数据进行过滤
点击增加按钮:
这是由 mutations下的addCounter函数进行修改的
点击异步增加按钮:
这是由actions下的asyncAddCounter函数异步调用api返回的数值为3,每点击一次就会增加3,同时进行一次异步获取API数据。
以下是一个例子
login.vue
xxxxxxxxxx
<template>
<div class="login_wrap">
<div class="form_wrap">
<el-form
ref="formRef"
:model = "loginData"
label-width = "100px"
class="demo-dynamic"
>
<el-form-item
prop = "username"
label = "用户名"
:rules = "[
{
required:true,
message:'此项为必填项',
trigger:'blur',
},
]"
>
<el-input v-model="loginData.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item
prop = "password"
label = "密码"
:rules = "[
{
required:true,
message:'此项为必填项',
trigger:'blur',
},
]"
>
<el-input type="password" v-model="loginData.password" placeholder="密码" />
</el-form-item>
<el-button type="primary" class="login_btn" @click="handleLogin">登录</el-button>
<p></p>
</el-form>
</div>
</div>
</template>
<script>
import { reactive,toRefs } from 'vue';
import { useStore } from 'vuex';
export default {
name: 'login',
setup(){
const store = useStore();
const count = store.state.number.count;
const data = reactive({
loginData: {
username: '',
password: '',
},
num:count,
})
console.log("修改器的getters",store.getters["number/countStatus"]);
function handleLogin(){
// store.commit('number/setCount',100);
store.dispatch('number/setCountPromise',100).then(res=>{
alert("修改成功");
}).catch(err=>{
alert(err)
});
console.log(store.state.number.count)
console.log("修改后getters:", store.getters["number/countStatus"]);
}
return {
toRefs(data),
handleLogin
}
}
}
</script>
<style scoped>
.login_wrap{
width: 100%;
height: 100vh;
background-color: rgb(56, 86, 139);
position: relative;
}
.form_wrap{
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
background-color: #fff;
padding: 30px 50px;
border-radius: 5px;
}
.login_btn{
margin: 0 auto;
display: block;
}
</style>
store/index.js
xxxxxxxxxx
import { createStore } from 'vuex'
import number from './state/num.state'
export default createStore({
//数据比较多,分模块
modules: {
number
}
})
store/state/num.state.js
xxxxxxxxxx
export default{
namespaced: true,
//全局状态初始化
state: {
count:1,
},
//计算state,获取相应的值
getters: {
countStatus(state){
return state.count <= 1
}
},
//更新状态的方法-更新state的唯一方法,commit mutations
mutations: {
setCount(state,num){
state.count = num
}
},
//可以异步操作,可以返回promise,更改数据还是传递到mutations去更改
actions: {
setCountPromise({commit},num){
return new Promise((resolve, reject)=>{
if(num>100){
reject('值不能大于100');
}else{
commit('setCount',num)
resolve();
}
})
}
},
}
组合式API
在vue2.x中通过组件data的方法来自定义一些当前组件的数据
xxxxxxxxxx
data(){
return{
name:"小杨",
list:[]
}
}
ref是简单型的数据使用率高,reactive是复杂性数据使用率高,功能类似
如下是引用数据
xxxxxxxxxx
<template>
<div class="hello">
<p></p>
<ul>
<li v-for="(item,index) in names.list" :key="index"></li>
</ul>
</div>
</template>
<script>
import { ref,reactive } from 'vue'
export default {
//组合式API
setup() {
//ref
const msg = ref('hello world');
//reactive
const names = reactive({
list:['张三','李四','王五']
});
return {
msg,
names
}
}
}
</script>
在vue2.x中methods来定义一些当前组件内部方法
xxxxxxxxxx
methods:{
http(){}
}
在vue3.x中直接在setup方法中定义并return
xxxxxxxxxx
setup(){
const http = ()=>{
//do something
}
return {
http
};
}
xxxxxxxxxx
<template>
<div class="hello">
<p></p>
<ul>
<li v-for="(item,index) in names.list" :key="index"></li>
</ul>
<button @click="clickHandle">按钮</button>
<p></p>
</div>
</template>
<script>
import { ref,reactive } from 'vue'
export default {
props:{
msg:String
},
//组合式API
setup(props,ctx) {
//setup中没有this
//setup中有context(ctx)
//ref
const message = ref('hello world');
//reactive
const names = reactive({
list:['张三','李四','王五']
});
function clickHandle() {
console.log("点击了");
//一定要通过msg.value修改数据
message.value = 'hello vue3';
}
const msg = props.msg;
return {
message,
names,
clickHandle,
msg
}
}
}
</script>
运行结果:
可以通过在生命周期钩子前面加上on
来访问组件的生命周期钩子。
下面是如何在setup()内部调用生命周期钩子
Options API | Hook inside setup |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
xxxxxxxxxx
<template>
<div class="hello">
</div>
</template>
<script>
import { onMounted } from 'vue';
export default {
setup(){
//比以前有优势,以前同一个生命周期函数只能存在一个,现在可以存在多个
onMounted(()=>{
console.log('生命周期Mounted1');
})
onMounted(()=>{
console.log('生命周期Mounted2');
})
}
}
</script>
注意:只能是父传子
用法
父组件
xxxxxxxxxx
<script>
import Child from './components/Child.vue'
import { provide } from 'vue';
export default {
components: {
HelloWorld
},
setup(){
provide('message','hello world')
}
}
</script>
子组件
xxxxxxxxxx
<script>
import { inject } from 'vue';
export default {
setup(){
const message = inject('message');
return {
message
}
}
}
</script>
地址:https://element-plus.org/zh-CN/component/overview.html
安装命令
xxxxxxxxxx
npm install element-plus --save
引用element-plus分为两种方式 完整引用和按需导入
完整引入打包时会使项目体积非常大
按需导入需要哪个引用哪个
完整引用
main.js
xxxxxxxxxx
import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).use(ElementPlus).mount('#app')
xxxxxxxxxx
<template>
<div class="hello">
<!-- 按钮 -->
<div class="mb-4">
<el-button>Default</el-button>
<el-button type="primary">Primary</el-button>
<el-button type="success">Success</el-button>
<el-button type="info">Info</el-button>
<el-button type="warning">Warning</el-button>
<el-button type="danger">Danger</el-button>
</div>
<!-- 开关 -->
<el-switch v-model="value1" />
<el-switch
v-model="value2"
class="ml-2"
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
/>
</div>
</template>
<script>
import { ref } from 'vue'
export default{
setup(){
const value1 = ref(true)
const value2 = ref(true)
return{
value1,
value2
}
}
}
</script>
运行效果:
按需导入
按照unplugin-vue-components和unplugin-auto-import这两个插件
命令:
xxxxxxxxxx
npm install -D unplugin-vue-components unplugin-auto-import
修改vue.config.js配置文件 直接覆盖即可
xxxxxxxxxx
const { defineConfig } = require('@vue/cli-service')
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
},
})
地址:https://element-plus.org/zh-CN/component/icon.html
1.安装element-plus
xxxxxxxxxx
npm install element-plus --save
2.安装字体图标
xxxxxxxxxx
npm install @element-plus/icons-vue
3.在项目根目录下,创建 plugins 文件夹,在文件夹下创建文件文件icons.js
icons.js
xxxxxxxxxx
import * as components from '@element-plus/icons-vue';
export default {
install: (app) => {
for (const key in components){
const componentConfig = components[key];
app.component(componentConfig.name, componentConfig);
}
},
};
4.在main.js中引入icons.js文件
xxxxxxxxxx
import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
// 全局引入ElementPlus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
// 引入图标elementIcon
import elementIcon from './plugins/icons';
//使用ElementPlus和图标elementIcon
createApp(App).use(ElementPlus).use(elementIcon).mount('#app')
使用图标即可
xxxxxxxxxx
<template>
<div class="hello">
<el-icon :size="50" color="lightblue"><Search /></el-icon>
</div>
<div style="font-size: 20px">
<!-- 由于SVG图标默认不携带任何属性 -->
<!-- 你需要直接提供它们 -->
<Edit style="width: 1em; height: 1em; margin-right: 8px" />
<Share style="width: 1em; height: 1em; margin-right: 8px" />
<Delete style="width: 1em; height: 1em; margin-right: 8px" />
<Search style="width: 1em; height: 1em; margin-right: 8px" />
</div>
</template>
<script>
export default {
}
</script>
运行效果:
xxxxxxxxxx
<template>
<h1>hello world</h1>
<p></p>
<p v-html="info"></p>
<p :data="dataVal">我有属性data</p>
<!-- 类名绑定 可以叠加使用-->
<p class="text" :class="{'red':isRed}">我是红色的</p>
</template>
<script>
import {reactive, toRefs} from 'vue'
export default{
name:"home",
setup(){
//相当于beforeCreate和created
const data = reactive({
name:"小红",
age:20,
info:"<i>我是斜体字</i>",
dataVal:20,
isRed:true
})
return{
//...toRefs(data)再使用就不用加data了
toRefs(data),
}
}
}
</script>
js获取焦点focus
失去焦点blur
改变change
xxxxxxxxxx
import {useRouter} from 'vue-router'
const router = useRouter();// 获取路由实例
const login = () => {
router.push({ path: '/login' });//使用路由跳转到登录界面
}
控制台报错
xxxxxxxxxx
WebSocketClient.js:13 WebSocket connection to 'ws://192.168.48.103:8080/ws' failed: Error in connection establishment: net::ERR_CONNECTION_TIMED_OUT
解决方法:在vue.config.js中加入
xxxxxxxxxx
devServer:{
host: '0.0.0.0',
port:8080,
client: {
webSocketURL: 'ws://0.0.0.0:8080/ws',
},
headers: {
'Access-Control-Allow-Origin': '*',
}
}
安装
xxxxxxxxxx
npm install lodash
导包
xxxxxxxxxx
import _ from 'lodash'
使用
xxxxxxxxxx
<el-input @input="debounceCheckYongHu" v-model="form.yonghu" input-width="80%" />
<script setup>
//声明防抖函数
let debounceCheckYongHu;
const test = ()=>{
//使用防抖函数,在功能函数里使用
debounceCheckYongHu = _.debounce(checkYongHu, 500); // 延迟500毫秒后执行
}
</script>
安装
xxxxxxxxxx
npm install cookie_js --save
存储
xxxxxxxxxx
cookie.set("key","value");
cookie.set({key1:"value1", key2:"value2"});
获取
xxxxxxxxxx
cookie.get("key");
cookie.get(['key1','key2']);
清除
xxxxxxxxxx
cookie.remove('key');
cookie.remove('key1', 'key2');
cookie.remove(['key1', 'key2']);
关闭浏览器自动清除,具有临时性
存储大小:5M
存储于客户端
只能存储字符串类型
存储
xxxxxxxxxx
window.seesionStorage.setItem("key","value");
获取
xxxxxxxxxx
window.sessionStorage.getItem("key");
删除
xxxxxxxxxx
window.sessionStorage.removeItem("key");
清空所有
xxxxxxxxxx
sessionStorage.clear();
需要手动清除,具有长期性
存储
xxxxxxxxxx
window.localStorage.setItem("key","value");
获取
xxxxxxxxxx
window.localStorage.getItem("key");
删除
xxxxxxxxxx
window.localStorage.remove("key");
清除所有
xxxxxxxxxx
localStorage.clear();
在router文件夹下index.js
1.给每一个路由加入meta标签
xxxxxxxxxx
{
path: '/login',
name: 'login',
component: () => import('../views/Login.vue'),
meta: {
title: '登录页',
keepAlive: true, // 需要被缓存
}
},
2.在main.js加入
xxxxxxxxxx
router.beforeEach((to, from, next) => {
//路由发生改变时,触发
window.document.title = to.meta.title == undefined ? '默认标题' : to.meta.title
if (to.meta.requireAuth) {
let token = Cookies.get('access_token');
let anonymous = Cookies.get('user_name');
if (token) {
next({
path: '/login',
query: {
redirect: to.fullPath
}
})
}
}
next();
})
1.在el-form里添加 @keydown.native.enter="onEnterPress"
xxxxxxxxxx
<el-form :model="loginData" @keydown.native.enter="onEnterPress">
2.在script里写onEnterPress函数
xxxxxxxxxx
const onEnterPress = (event) => {
// 避免表单内的textarea元素触发登录
if (event.target.tagName.toLowerCase() !== 'textarea') {
toLogin();
}
}