目录
vue项目的创建
安装vue工具vue cli
vue cli是vue.js的开发标准工具,vue cli是一个基于vue.js进行快速开发的完整系统
全局安装vue cli:npm install -g @vue/cli
验证安装是否成功:vue -V
创建一个项目
npx vue create vue-demo
注意:项目名无论如何都不能出现大写,必须全小写,但是可以用-来链接多个单词
注意:
- 在控制台中可以用上下按键来调整选择项
- 在控制台中,可以使用空格选择是否选中或取消
- 我们选择第三个手动选择特性
我们如图选中即可,注意关闭linter/formatter校检
我们选择版本3
询问配置文件的存放位置,我们选择第一个
是否保存预设,我选n
用什么包管理-npm因为使得熟
运行项目
- 进入项目根目录:cd vue-demo
- 运行npm run serve启动项目
进入local:后面的地址(其实哪个都行)
vue架构理解
index.html
<!DOCTYPE html>
<html lang="">
<!-- public内的index.html总页面内 -->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 网站图标 -->
<link rel="icon" href="./view.jpg">
<!-- 网站标题,可直接修改 -->
<title>vue实验</title>
</head>
<body>
<!-- main.js全局vue挂载区(AppInner组件的地方) -->
<div id="app"></div>
</body>
</html>
修改vue网站的图标
//vue.config.js文件内
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 修改vue网站的图标
pwa:{
iconPaths:{
favicon32:"view.jpg",
favicon16:"view.jpg",
appleTouchicon:"view.jpg",
maskicon:"view.jpg",
msTileImage:"view.jpg"
}
}
})
main.js
// src下的main.js(全局js文件内)
//导入vue
import { createApp } from 'vue'
//导入AppInner组件,使其成为全局vue挂载区(提供视图,用于翻页)
import AppInner from './AppInner.vue'
import './registerServiceWorker'
// 导入的路由——里面可以导入许多组件和实现路由跳转
import router from './router/index'
// vuex——实现vue的数据管理、过滤、修改、提交等
import store from './store'
// 导入elementplus组件
import ElementPlus from 'element-plus'
// 引入elementplus的样式css
import 'element-plus/dist/index.css'
//注册组件(对于app/AppInner的全局vue对象)
createApp(AppInner).use(store).use(router).use(ElementPlus,{size:"small"}).mount('#app')
单文件组件
Vue单文件组件(又名.vue文件,缩写为SFC)是一种特殊的文件格式,他允许将Vue组建的模板、逻辑与样式封装在单个文件中
<template>
<h3>我是单文件组件</h3>
</template>
<script>
export default{
name:"MyConponent"/* 组件名称 */
}
</script>
<!-- scoped属性表示css标签样式只在本组件内有效 -->
<style scoped>
h3{
background: red;
}
</style>
scoped属性:该属性表示css标签样式只在当前组件内有效,对于本组件内引入的其他组件、以及其他组件无效(不然每个被“全局”导入的组件内的css样式都会全局生效)
全局导入:在main.js文件内导入或者被导入的文件可以连接到main.js文件全局
组件内部元素
- template:视图结构部分
- script:逻辑部分
- style:样式部分
加载组件
- 第一步:引入组件:import HelloWorld from './components/HelloWorld.vue'
- 第二步:挂载组件:components: {HelloWorld}
- 第三步:显示组件:<HelloWorld></HelloWorld>
组件的组织
通常一个应用会以一棵嵌套的组件树的形式来组织
props组件交互
props是可以在组件之上进行传递数据的
父组件给子组件传值
<template>
<my-component :message="message" :age="age" :names="names"></my-component><!-- 这样写也可以 -->
</template>
<script>
import MyComponent from './components/MyComponent.vue'
export default {
name: 'App',
data() {
return {
message:"你好,世界",
age:20,
names:["lili","makabaka","tangbulibou"]
}
},
components: {
MyComponent
}
}
</script>
子组件接收值
<template>
<h3>props传递数据</h3>
<p>{{message}}</p>
<p>{{age}}</p>
<ul>
<li v-for="(item,index) in names" :key="index">{{item}}</li>
</ul>
</template>
<script>
export default{
name:"MyConponent",/* 组件名称 */
props:{
message:{
type:String,/* 组件接受的值为string类型 */
default:""/* 如果不传值则组件默认值为空串 */
},
age:{
type:Number,
default:0
},
names:{
type:Array,
/* 数组和对象必须使用函数进行返回 */
default:function(){
return []
}
}
}
}
</script>
子组件向父组件传递数据
子组件内
<template>
<div class="school">
<button @click="sendSchoolName(name)">把学校名给App</button>
</div>
</template>
<script>
export default {
name:"School",
props:['getSchoolName'],
data(){
return{
name:"bilibili",
address:"北京"
}
},
methods:{
sendSchoolName(name){
this.getSchoolName(name)
}
}
}
</script>
父组件内
<template>
<div class="app">
<!-- 通过父组件给子组件传递函数类型的prop参数实现了子传父 -->
<School :getSchoolName="getSchoolName"></School>
</div>
</template>
<script>
import School from "./components/School.vue";
export default {
name: "App",
components: {
School
},
methods: {
getSchoolName(name) {
console.log("app收到了学校名:" + name);
}
}
}
</script>
理解:两个组件通过v-bind:进行连接,子组件通过props属性对床过来的数据进行接收
祖先组件给子孙组件传递数据
使用方法:父组件有一个provide选项用来提供数据,子组件有一个inject选项用来接收这个数据
结果:无论组件嵌套有多深,子孙组件均可接收祖先组件传来的数据
需求:second组件要接收app组件的msg,name
<template>
<div>
<!-- app组件内 -->
<First></First>
<h1>父组件的{{obj.msg}}</h1>
<h1>父组件的{{name}}</h1>
<button @click="obj.msg='hiworld'">父组件改变msg</button>
<button @click="name='lala'">父组件改变name</button>
</div>
</template>
<script>
import First from './components/First.vue';
export default {
data(){
return{
name:"lili",
//msg:"helloworld",//不能实现父子响应式
//传递响应式对象,以使改变父组件属性的同时改变父组件传给子组件的属性
obj:{
msg:"helloworld",
}
}
},
//祖先组件向子孙组件传递数据
//provide:{msg:"helloworld"},//此种传递方式不能直接传变量(只能传具体的值)
provide(){
//若想要传递具体的变量则需要provide返回一个对象
return{
obj:this.obj,//将响应式对象传递过去
name:()=>this.name//通过函数返回响应式
}
},
components:{
First
}
}
</script>
<template>
<div id="first">
<Second></Second>
</div>
</template>
<script>
import Second from './Second.vue';
export default {
components: {
Second
}
}
</script>
<template>
<div id="second">
<h1>子组件的{{obj.msg}}</h1>
<!-- 通过函数接收 -->
<h1>子组件的{{name()}}</h1>
</div>
</template>
<script>
export default {
//子孙组件接收祖先组件传过来的值
inject:['obj','name']
}
</script>
自定义事件的组件交互
自定义事件可以在组件中反向传递,props可以将数据从父组件传递到子组件,那么反向如何操作呢,就可以利用自定义事件实现$emit
子组件传递参数
<template>
<h3>自定义事件的组件交互</h3>
<button @click="sendClickHandle()">点击传递事件</button>
</template>
<script>
export default{
name:"MyConponent",/* 组件名称 */
data() {
return {
message:"我变大神,那是必然"
}
},
methods: {
sendClickHandle(){
/* 参数1:字符串-事件名称
参数2:传递的数据-事件参数 */
//触发my-component组件的实例对象绑定的onevent事件并传递参数
this.$emit("onevent",this.message)
}
},
}
</script>
父组件接收子组件参数
<template>
<!-- 给my-component组件的实例对象绑定onevent事件 -->
<!--里面的.once标识只能触发一次(若没有该参数可触发无数次)-->
<my-component @onevent.once="getDataHandle"></my-component><!-- 这样写也可以 -->
</template>
<script>
import MyComponent from './components/MyComponent.vue'
export default {
name: 'App',
components: {
MyComponent
},
methods: {
/* 里面的参数为自定义事件传递数据的参数 */
getDataHandle(data){
console.log(data);
}
},
}
</script>
理解:点击事件触发sendClickHandle函数,此函数触发自定义事件onevent并将参数带入父组件,父组件事件已被触发调用带参数的getDataHandle方法来获得数据
vue全局事件总线
前言:全局事件总线常用于兄弟组件的传值
vue项目内安装mitt:npm install -S mitt
//main.js内引入并使用
import mitt from "mitt"
//紧接着定义全局属性
const app = createApp(App);
app.config.globalProperties.$bus = new mitt();
父组件app内
<template>
<div class="app">
<Student />
<School />
</div>
</template>
<script>
import Student from "./components/Student.vue";
import School from "./components/School.vue";
export default {
name: "App",
components: {
Student,
School,
},
}
</script>
需求:子组件school向student组件发送数据
子组件school内
<template>
<div class="school">
<button @click="sendSchoolName(name)">把学校名给student</button>
</div>
</template>
<script>
export default {
name:"School",
data(){
return{
name:"bilibili",
address:"北京"
}
},
methods:{
sendSchoolName(name){
//触发公共能找到的全局事件总线事件
this.$bus.emit("onevent",name)
}
}
}
</script>
子组件student内
<template>
<div class="student">
<button @click="over">取消事件总线事件绑定</button>
</div>
</template>
<script>
export default {
name:"student",
methods:{
over(){
//取消事件总线的事件绑定
this.$bus.off("onevent")
}
},
mounted(){
// 监听公共全局事件总线的onevent事件,并接受参数(参数可以为多个)
this.$bus.on("onevent",(data)=>{
console.log("学生得到了学校信息"+data);
})
}
}
</script>
组件的生命周期
每个组件在被创建时都要经过一系列的初始化过程--例如,需要设置数据监听、编译模板、将实例挂载到dom并在数据变化时更新dom等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己代码的机会
生命周期时的状态
- 创建时:beforeCreate、created
- 渲染时:beforeMount、mounted
- 更新时:beforeUpdate、updated
- 卸载时:beforeUnmount、unmounted
<template>
<h3>生命周期函数</h3>
<input type="text" v-model.lazy="msg">
<p>{{msg}}</p>
</template>
<script>
export default{
name:"mycomponent",
data() {
return {
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>
vue引入第三方
以swiper为例
- swiper开源、免费、强大的触摸滑动插件
- swiper是纯javascript打造的滑动特效插件,面向手机、平板电脑等移动终端
- swiper能实现触摸焦点图、触屏tab切换、触屏轮播图切换等常用效果
安装swiper:npm i --save swiper
<template>
<div id="a">
<!-- 使用轮播图-->
<swiper>
<swiper-slide class="mySwiper">
<img src="../assets/test.jpg" alt="图片没了">
</swiper-slide>
<swiper-slide>
<img src="../assets/test.jpg" alt="图片没了">
</swiper-slide>
<swiper-slide>
<img src="../assets/test.jpg" alt="图片没了">
</swiper-slide>
</swiper>
</div>
</template>
<script>
// 引入轮播图组件
import {Swiper,SwiperSlide} from "swiper/vue"
import "swiper/css"
export default{
name:"mycomponent",
// 注册组件
components:{
Swiper,
SwiperSlide,
}
}
</script>
vuex状态管理
vuex简介
概念:专门在vue中实现集中式状态(数据)管理的一个vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间的通信
注意:vuex不属于任意一个组件里
什么时候用vuex
- 多个组件依赖于同一个状态
- 来自不同组件的行为需要变更同一个状态
vuex的工作原理
注意:
- 将数据交给vuex管理其是就是把数据交给State对象进行管理
- 整个过程为vue组件通过dispatch函数做出某种行为,actions对其行为进行提交,mutations对行为进行执行并加工到state中然后就会渲染到该vue组件
- 整个过程的actions、mutations、state都是经过store管理,store为他们提供对应的函数
vuex的使用
安装vuex:npm install --save vuex
创建store对象
在src目录中新建一个store文件夹存放index.js文件
//index.js文件内
import { createStore } from 'vuex'
// vuex的核心作用就是帮我们管理组件之间的状态
export default createStore({
state: {
count:10
},
getters: {
getCount(state){
/* count数据深层的管理 */
return state.count>0?state.count:"count数据异常"
}
},
mutations: {
//用于修改参数(第一个传state信息,第二个传修改参数的变量)
//注意:在mutations里面不可以执行异步的代码
addCount(state,num){
state.count+=num
}
},
actions: {
//在actions里面可以执行异步的代码,num为携带的参数
addAsync(context,num){
//commit内的参数为mutations内的函数
setTimeout(()=>{
//异步内触发mutations函数
context.commit("addCount",num)
},1000)
}
},
})
引入vuex
//main.js内
import { createApp } from 'vue'
import App from './App.vue'
import './registerServiceWorker'
//全局引入vuex
import store from './store'
//注册vuex
createApp(App).use(store).mount('#app')
vuex状态管理的核心
常用的核心概念包括:State、Getter、Mulation、Action
app.vue内
<template>
<HelloWorld></HelloWorld>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld,
}
}
</script>
State
作用:在组件中读取状态
方式一
<template>
<div class="hello">
<!-- 从vuex中读取属性 -->
{{$store.state.count}}
</div>
</template>
方式二
<template>
<div class="hello">
<!-- 直接读取count -->
{{count}}
</div>
</template>
<script>
//引入mapState从vuex
import { mapState } from 'vuex'
export default {
name: 'HelloWorld',
//计算属性
computed:{
...mapState(["count"])
}
}
</script>
Getter
作用:对vuex中的数据进行过滤(对读取的方式提供一种更合理的方式)
方式一
<template>
<div class="hello">
{{$store.getters.getCount}}
</div>
</template>
方式二
<template>
<div class="hello">
<!-- 直接获取 -->
{{getCount}}
</div>
</template>
<script>
/* 引入mapgetters */
import { mapGetters } from "vuex";
export default {
name: 'HelloWorld',
//计算属性
computed:{
...mapGetters(["getCount"])
}
}
</script>
Mutation
理解:更改vuex的store中状态的唯一方法是提交mutation。vuex中的mutation非常类似于事件:每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler),这个回调函数就是我们实际进行状态更改的地方,并且他会接收state作为第一个参数
方式一
<template>
<div class="hello">
<!-- 获取属性对比 -->
<p>count={{$store.getters.getCount}}</p>
<!-- mutaion的事件类型 -->
<button @click="addClickHandler">增加</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
methods: {
/* mutation调用函数 */
addClickHandler(){
//固定调用方式
//第一个参数为函数,第二个参数为修改参数的变量num
this.$store.commit("addCount",15)
}
},
}
</script>
方式二
<template>
<div class="hello">
<p>count={{$store.getters.getCount}}</p>
<!-- mutaion的事件类型 -->
<button @click="addClickHandler">增加</button>
</div>
</template>
<script>
/* 引入mapMutations */
import { mapMutations } from "vuex";
export default {
name: 'HelloWorld',
methods: {
/* mutation调用函数 */
...mapMutations(["addCount"]),
addClickHandler(){
//里面的参数为修改参数的变量(num)
this.addCount(20)
}
},
}
</script>
Action
理解:Action用于处理异步任务,若通过异步操作变更数据,而不能用Mutation,但是在Action中还要通过触发Mutation的方式间接变更数据
方式一
<template>
<div class="hello">
<p>count={{$store.getters.getCount}}</p>
<button @click="addAsyncClickHandler">异步增加</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
methods: {
addAsyncClickHandler(){
/* 固定写法,第一种方式 */
this.$store.dispatch("addAsync",20)
}
},
}
</script>
方式二
<template>
<div class="hello">
<p>count={{$store.getters.getCount}}</p>
<button @click="addAsyncClickHandler">异步增加</button>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
name: 'HelloWorld',
methods: {
...mapActions(["addAsync"]),
addAsyncClickHandler(){
/* 第二种方式 */
//里面传num
this.addAsync(15);
}
},
}
</script>
fragment-碎片
vue3的新特性,不再限于模板中只有一个根节点
<template>
<div>道生一</div>
<div>一生二</div>
</template>