使用cmd输入以下命令
npm install -g @vue/cli# 验证是否安装成功vue --version创建一个项目
xxxxxxxxxx#项目名称my-project不能出现大写但是可以用“-”进行连接vue create my-projectxxxxxxxxxxvue create vue-demo项目名称不要出现大写
cd到项目里,然后使用
xxxxxxxxxxnpm run servev-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
xxxxxxxxxxclickP(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示例
xxxxxxxxxximport { 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
xxxxxxxxxximport { 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开源 免费 强大 自适应的触摸滑动插件
xxxxxxxxxx1.安装最新swipernpm i swiper --save
安装指定版本的swipernpm i swiper@8.1.6 --savexxxxxxxxxx<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>运行结果:

xxxxxxxxxxAxios需要安装并不是vue内部集成的npm install --save axios
字符串的转换安装包npm install --save querystring在main.js进行全局引用,日后开发需要很多用到网络请求的地方,因此进行全局引用
前提:已进行跨域
xxxxxxxxxximport axios from 'axios'const app = createApp(App)app.config.globalProperties.$axios = axiosapp.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
xxxxxxxxxximport 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
xxxxxxxxxxconst base = { baseUrl:"http://localhost:5001/", peopleAdmin:"api/peopleAdmin", delPeopleAdmin:"api/delPeopleAdmin"}
export default base;index.js
xxxxxxxxxximport 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';//GETapi.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)}) //POSTapi.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
xxxxxxxxxxfrom flask import Flaskfrom views.peopleAdmin import getPeopleAdminTableData,delPeopleAdminTableDatafrom 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
xxxxxxxxxxfrom flask import jsonify,requestimport jsonfrom conn_sql import conndef 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需要放入产生跨域的域名即可
xxxxxxxxxxdevServer:{ 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>运行结果:

xxxxxxxxxx1.安装路由命令npm install --save vue-router在src下创建router文件夹
2.1 router下创建index.js
index.js
xxxxxxxxxximport { 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
xxxxxxxxxximport { 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
xxxxxxxxxximport { 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 routerviews文件夹下
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
xxxxxxxxxximport { 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
xxxxxxxxxximport { 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
xxxxxxxxxximport { createStore } from 'vuex'
// Vuex的核心就是帮我们管理组件之间的数据(状态)的export default createStore({ //所有的数据(状态)都放在state里 state: { counter: 0 }})3.main.js中引入index.js
main.js
xxxxxxxxxximport { 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
xxxxxxxxxximport { 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中的storeconst 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
xxxxxxxxxximport { 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
xxxxxxxxxxconst { 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
xxxxxxxxxximport { createStore } from 'vuex'import number from './state/num.state'export default createStore({ //数据比较多,分模块 modules: { number }})store/state/num.state.js
xxxxxxxxxxexport 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的方法来自定义一些当前组件的数据
xxxxxxxxxxdata(){ 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来定义一些当前组件内部方法
xxxxxxxxxxmethods:{ http(){}}在vue3.x中直接在setup方法中定义并return
xxxxxxxxxxsetup(){ 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
安装命令
xxxxxxxxxxnpm install element-plus --save引用element-plus分为两种方式 完整引用和按需导入
完整引入打包时会使项目体积非常大
按需导入需要哪个引用哪个
完整引用
main.js
xxxxxxxxxximport { 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这两个插件
命令:
xxxxxxxxxxnpm install -D unplugin-vue-components unplugin-auto-import修改vue.config.js配置文件 直接覆盖即可
xxxxxxxxxxconst { 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
xxxxxxxxxxnpm install element-plus --save2.安装字体图标
xxxxxxxxxxnpm install @element-plus/icons-vue3.在项目根目录下,创建 plugins 文件夹,在文件夹下创建文件文件icons.js
icons.js
xxxxxxxxxximport * 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文件
xxxxxxxxxximport { createApp } from 'vue'import App from './App.vue'import './registerServiceWorker'
// 全局引入ElementPlusimport ElementPlus from 'element-plus'import 'element-plus/dist/index.css'// 引入图标elementIconimport elementIcon from './plugins/icons';//使用ElementPlus和图标elementIconcreateApp(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
xxxxxxxxxximport {useRouter} from 'vue-router'
const router = useRouter();// 获取路由实例const login = () => { router.push({ path: '/login' });//使用路由跳转到登录界面}
控制台报错
xxxxxxxxxxWebSocketClient.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中加入
xxxxxxxxxxdevServer:{ host: '0.0.0.0', port:8080, client: { webSocketURL: 'ws://0.0.0.0:8080/ws', }, headers: { 'Access-Control-Allow-Origin': '*', }}
安装
xxxxxxxxxxnpm install lodash导包
xxxxxxxxxximport _ 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>
安装
xxxxxxxxxxnpm install cookie_js --save存储
xxxxxxxxxxcookie.set("key","value");cookie.set({key1:"value1", key2:"value2"});获取
xxxxxxxxxxcookie.get("key");cookie.get(['key1','key2']);清除
xxxxxxxxxxcookie.remove('key');cookie.remove('key1', 'key2');cookie.remove(['key1', 'key2']);关闭浏览器自动清除,具有临时性
存储大小:5M
存储于客户端
只能存储字符串类型
存储
xxxxxxxxxxwindow.seesionStorage.setItem("key","value");获取
xxxxxxxxxxwindow.sessionStorage.getItem("key");删除
xxxxxxxxxxwindow.sessionStorage.removeItem("key");清空所有
xxxxxxxxxxsessionStorage.clear();需要手动清除,具有长期性
存储
xxxxxxxxxxwindow.localStorage.setItem("key","value");获取
xxxxxxxxxxwindow.localStorage.getItem("key");删除
xxxxxxxxxxwindow.localStorage.remove("key");清除所有
xxxxxxxxxxlocalStorage.clear();
在router文件夹下index.js
1.给每一个路由加入meta标签
xxxxxxxxxx{ path: '/login', name: 'login', component: () => import('../views/Login.vue'), meta: { title: '登录页', keepAlive: true, // 需要被缓存 } },2.在main.js加入
xxxxxxxxxxrouter.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函数
xxxxxxxxxxconst onEnterPress = (event) => { // 避免表单内的textarea元素触发登录 if (event.target.tagName.toLowerCase() !== 'textarea') { toLogin(); }}