Vue—前端框架
目录
Vue
渐进式 JavaScript 框架
通过对框架的了解与运用程度,来决定其在整个项目中的应用范围,最终可以独立以框架方式完成整个web前端项目
一、走进Vue
1、what -- 什么是Vue
可以独立完成前后端分离式web项目的JavaScript框架
2、why -- 为什么要学习Vue
三大主流框架之一:Angular React Vue 先进的前端设计模式:MVVM 可以完全脱离服务器端,以前端代码复用的方式渲染整个页面:组件化开发
3、special -- 特点
单页面web应用 数据驱动 数据的双向绑定 虚拟DOM
4、how -- 如何使用Vue
- 开发版本:vue.js
- 生产版本:vue.min.js
<div id="app"> {{ }} </div> <script src="js/vue.min.js"></script> <script> new Vue({ el: '#app' }) </script>
二、Vue实例
1、el:实例
new Vue({ el: '#app' }) // 实例与页面挂载点一一对应 // 一个页面中可以出现多个实例对应多个挂载点 // 实例只操作挂载点内部内容 // el的值为css3选择器,选择结果为第一个,不会匹配其他的,所以一般用id选择器 // html、body不能作为挂载点
2、data:数据
1、实例的数据属性一般都在data中定义
2、在页面中,插值表达式{{}}
可以引用变量的值,即vue实例的属性值
3、在外部可以通过vue对象名.属性
的方式获取属性值,在vue对象内部,通过this.属性
的方式获取属性值
<div id='app'> {{ msg }} </div> <script> var app = new Vue({ el: '#app', data: { msg: '数据', } }) console.log(app.$data.msg); console.log(app.msg); </script>
3、methods:方法
<style> .box { background-color: orange } </style> <div id='app'> <p class="box" v-on:click="pClick">测试</p> <p class="box" v-on:mouseover="pOver">测试</p> </div> <script> var app = new Vue({ el: '#app', methods: { pClick () { // 点击事件的回调函数 }, pOver () { // 悬浮事件的回调函数 } } }) </script> <!-- 了解v-on:为事件绑定的指令 --> <!-- methods为事件提供实现体-->
4、computed:计算
1、computed计算属性可以声明方法属性,但是该方法属性一定不能在data中重复声明
2、该方法属性必须在页面中渲染,才会启用该属性绑定的方法,方法属性的值就是绑定方法的返回值
3、在该方法中,所有的变量都会被监听到,任何一个变量发生变化,都会触发该绑定方法,从而更新方法属性的值,不管这个值受不受到变化
<div id="app"> <input type="text" v-model="a"> <input type="text" v-model="b"> <div> {{ c }} </div> </div> <script> // 一个变量依赖于多个变量 new Vue({ el: "#app", data: { a: "", b: "", }, computed: { c: function() { // this代表该vue实例 return this.a + this.b; } } }) </script>
5、watch:监听
<div id="app"> <input type="text" v-model="ab"> <div> {{ a }} {{ b }} </div> </div> <script> // 多个变量依赖于一个变量 new Vue({ el: "#app", data: { ab: "", a: "", b: "", }, watch: { ab: function() { // 逻辑根据需求而定 this.a = this.ab[0]; this.b = this.ab[1]; } } }) </script>
6、delimiters:分隔符
<div id='app'> ${ msg } </div> <script> new Vue({ el: '#app', data: { msg: 'message' }, delimiters: ['${', '}'] }) </script>
三、生命周期钩子
- 表示一个vue实例从创建到销毁的这个过程,将这个过程的一些时间节点赋予了对应的钩子函数
- 钩子函数: 满足特点条件被回调的方法
new Vue({ el: "#app", data: { msg: "message" }, beforeCreate () { console.log("实例创建之前"); console.log(this.msg }, created () { console.log("实例创建成功, data, methods已拥有"); console.log(this.msg); }, beforeMounted () { console.log("渲染页面之前,data还可以更新") console.log(this.msg); }, mounted () { console.log("页面已被vue实例渲染, data, methods已更新"); } // 拿到需求 => 确定钩子函数 => 解决需求的逻辑代码块 })
四、Vue指令
1、文本相关指令
<div id="app"> <p>{{ msg }}</p> <!-- 插值表达式 --> <p v-text='msg'>原文本</p> <!--原文本会被msg替换 --> <p v-html='msg'></p> <!-- 可以解析带html标签的文本信息 --> <p v-once>{{ msg }}</p> <!-- v-once控制的标签只能被赋值一次 --> </div> <script type="text/javascript"> // 指令: 出现在html标签中可以被vue解析处理的全局属性 new Vue({ el: "#app", data: { msg: "message" } }) </script>
2、斗篷指令
<style type="text/css"> [v-cloak] { display: none; } </style> <div id="app" v-cloak> {{ msg }} </div> <script src="js/vue.min.js"></script> <script type="text/javascript"> new Vue({ el: "#app", data: { msg: "message" } }) </script> <!-- 避免页面闪烁-->
3、属性指令
<!-- 给自定义全局属性绑定变量 --> <p v-bind:abc="abc"></p> <!-- 以原字符串形式绑定全局属性 --> <p v-bind:title="'abc'"></p> <!-- 单类名绑定 --> <p v-bind:class="c1"></p> <!-- 多类名绑定 --> <p v-bind:class="[c2, c3]"></p> <!-- 类名状态绑定 --> <p v-bind:class="{c4: true|false|var}"></p> <!-- 多类名状态绑定 --> <p v-bind:class="[{c5: true}, {c6: flase}]"></p> <!-- 样式绑定 --> <!--绑定变量--> <div :style="div_style"></div> <!--直接绑定值,属性变为驼峰体--> <div :style="{width: '100px', height: '100px', backgroundColor: 'blue'}"></div> <script type="text/javascript"> new Vue({ el:"#app", data: { abc: "abc", c1: "p1", c2: "p2", c3: "p3", div_style: { width: "200px", height: "200px", backgroundColor: "cyan" } } }) </script> <!-- v-bind: 指令可以简写为 : -->
4、事件指令
<!-- v-on: 指令 简写 @ --> <!-- 不传参事件绑定,但事件回调方法可以获取事件对象 --> <p @click="fn"></p> <!-- ()可以传入具体实参 --> <p @click="fn()"></p> <!-- ()情况下,事件对象应该显式传入 --> <p @click="fn($event)"></p> <!-- -->
5、表单指令
1、双向绑定v-model:服务于文本输入框
2、单选框:v-model存储的值为单选框的value值
3、单一复选框:v-model存储的值为true|false或自定义替换的值
4、多复选框:v-model存储的值为多复选框value的数组,加入数组的顺序是点击选项的顺序
<div id="app"> <!-- v-model针对于表单元素 --> <form action="" method="get"> <!-- 1、双向绑定:服务于文本输入框 --> <!-- v-model存储的值为输入框的value值 --> <div> <input type="text" name="usr" v-model="in_val"> <input type="password" name="ps" v-model="in_val" > <textarea name="info" v-model="in_val"></textarea> </div> <!-- 2、单选框 --> <div> <!-- 单选框是以name进行分组,同组中只能发生单选 --> <!-- v-model存储的值为单选框的value值 --> 男:<input type="radio" name="sex" value="男" v-model="ra_val"> 女:<input type="radio" name="sex" value="女" v-model="ra_val"> {{ ra_val }} </div> <!-- 3、单一复选框 --> <!-- v-model存储的值为true|false --> <!-- 或者为自定义替换的值 --> <div> <input type="checkbox" v-model='sin_val' true-value="选中" false-value="未选中" /> {{ sin_val }} </div> <!-- 4、多复选框 --> <!-- v-model存储的值为多复选框value的数组 --> <div> <input type="checkbox" value="喜好男的" name="cless" v-model='more_val' /> <input type="checkbox" value="喜好女的" name="cless" v-model='more_val' /> <input type="checkbox" value="不挑" name="cless" v-model='more_val' /> {{ more_val }} </div> </form> </div> <script type="text/javascript"> new Vue({ el: '#app', data: { in_val: '', // 默认值可以决定单选框默认选项 ra_val: '男', // 默认值为true,单一复选框为选中,反之false为不选中 sin_val: '', // 数组中存在的值对应的复选框默认为选中状态 more_val: ['喜好女的','不挑'] } }) </script>
6、条件指令
1、直接在标签内书写v-if="条件1"
或v-else-if="条件2"
或v-else
,满足条件才会有该标签,否则没有该标签
2、v-show="条件"
,条件为真,渲染,条件为假隐藏标签,即令属性display为none,实际上在前端是有该标签的,不利于该标签包裹的数据的保护,但是有利于状态多变情况下的页面渲染
<div id="app"> <button @click="toggle">显隐切换</button> <!-- v-if --> <div class="box r" v-if="isShow"></div> <!-- v-show --> <div class="box o" v-show="isShow"></div> <!-- 1.条件渲染的值为true|false --> <!-- 2.true代表标签显示方式渲染 --> <!-- 3.false v-if不渲染到页面,v-show以display:none渲染到页面,但也不会显示 --> <!-- v-if v-else-if v-else 案例 --> <ul> <li @mouseover="changeWrap(0)">red</li> <li @mouseover="changeWrap(1)">green</li> <li @mouseover="changeWrap(2)">blue</li> </ul> <!-- red页面逻辑结构 --> <div class="wrap red" v-if="tag == 0" key="0">...</div> <!-- green页面逻辑结构 --> <div class="wrap green" v-else-if="tag == 1" key="1">...</div> <!-- blue页面逻辑结构 --> <div class="wrap blue" v-else key="2">...</div> <!-- v-if相关分支操作,在未显示情况下,是不会被渲染到页面中 --> <!-- 通过key全局属性操作后,渲染过的分支会建立key对应的缓存,提高下一次渲染速度 --> <!-- v-show 案例 --> <ul> <li @mouseover="changeMain(0)">red</li> <li @mouseover="changeMain(1)">green</li> <li @mouseover="changeMain(2)">blue</li> </ul> <!-- red页面逻辑结构 --> <div class="main red" v-show="whoShow(0)">...</div> <!-- green页面逻辑结构 --> <div class="main green" v-show="whoShow(1)">...</div> <!-- blue页面逻辑结构 --> <div class="main blue" v-show="whoShow(2)">...</div> </div> <script type="text/javascript"> new Vue({ el: "#app", data: { isShow: false, tag: 0, flag: 0 }, methods: { toggle () { this.isShow = !this.isShow; }, changeWrap (num) { this.tag = num; }, changeMain (num) { // this.flag num this.flag = num; }, whoShow (num) { // this.flag num return this.flag == num; } } }) </script>
7、循环指令
<!-- 语法:v-for="item in items" 1、v-for遍历数组或字符串时,v-for="v in arr|str" 或者v-for="(v,i) in arr|str" 2、v-for遍历字典时,v-for="(v,k,i) in dic" 3、js中,字典的key是有序的,value是无序的 --> <div id="app"> <h1>{{ msg }}</h1> <!--列表--> <ul> <li v-for="(n, i) in list" :key="i">value:{{ n }} | index: {{ i }}</li> </ul> <!--字典--> <ul> <li v-for="(v, k, i) in dic" :key="k">value:{{ v }} | key:{{ k }} | index: {{ i }}</li> </ul> <!-- 遍历的嵌套 --> <div v-for="(person, index) in persons" :key="index" style="height: 21px;"> <div v-for="(v, k) in person" :key="k" style="float: left;">{{ k }} : {{ v }} </div> </div> </div> <script type="text/javascript"> new Vue({ el: "#app", data: { msg: "列表渲染", list: [1, 2, 3, 4, 5], dic: { name: 'zero', age: 88888, gender: 'god' }, persons: [ {name: "zero", age: 8}, {name: "egon", age: 78}, {name: "liuXX", age: 77}, {name: "yXX", age: 38} ] } }) </script>
8、todolist案例
<div id="app"> <div> <input type="text" v-model="val"> <button type="button" @click="submitMsg">提交</button> </div> <ul> <li v-for="(v, i) in list" :key="i" @click="removeMsg(i)">{{ v }}</li> </ul> {{ list }} </div> <script type="text/javascript"> new Vue({ el: "#app", data: { val: "", list: [] }, methods: { submitMsg () { if (this.val) { this.list.push(this.val); this.val = "" } }, removeMsg(index) { this.list.splice(index, 1) } } }) </script>
五、组件
- 每一个组件都是一个vue实例
- 每个组件均具有自身的模板template,根组件的模板就是挂载点
- 每个组件模板只能拥有一个根标签
- 子组件的数据具有作用域,以达到组件的复用
1、根组件
<!-- 1、通过new Vue创建的实例就是根组件(实例与组件一一对应,一个实例就是一个组件) 2、每个组件组件均拥有模板,template 3、模板: 指的是用字符串形式定义的具有唯一根标签的html代码块,是组件成员template的值 如果template有非空值,就会替代组件的挂载点 --> <div id="app"> <h1>{{ msg }}</h1> </div> <script type="text/javascript"> var app = new Vue({ // 使用new Vue()来定义组件就是根组件 el: "#app", data : { msg: "根组件" }, template: "<div>显式模板</div>" // 当template不为空时,就会替代挂载点 }) // app.$template </script>
2、局部组件
<!-- 1、子组件也是vue实例,拥有除了el之外的所有成员,子组件的所有名字只能通过组件本身控制 2、子组件通过成员template以字符串的形式定义真正的标签 3、子组件通过成员data声明变量,为了保证复用子组件后的数据互不影响,使用函数包裹每个被调用的组件的变量名 4、在根组件中使用子组件名为标签调用子组件,若子组件是局部组件,则需要在根组件中注册过才能调用 --> <div id="app"> <local-tag></local-tag> <!--调用局部子组件--> <local-tag></local-tag> </div> <script> var localTag = { // 定义局部子组件 data () { // 声明子组件的变量,并利用函数隔离相同组件的数据 return { count: 0 } }, template: '<button @click="btnAction">局部{{ count }}</button>', // 定义子组件的标签 methods: { btnAction () { this.count ++ } } } new Vue({ el: "#app", components: { 'local-tag': localTag // 注册子组件,只能调用注册过的子组件 } }) </script>
3、全局组件
<!-- 1、全局组件也是子组件,必须在根组件之下才能调用 2、所有的跟组件都可以使用全部组件为子组件,不需要注册,而局部组件需要注册 3、除此之外,全局组件与局部组件没有差别 --> <div id="app"> <global-tag></global-tag> <global-tag></global-tag> </div> <script> Vue.component('global-tag', { // 定义全局组件 data () { return { count: 0 } }, template: '<button @click="btnAction">全局{{ count }}</button>', methods: { btnAction () { this.count ++ } } }) new Vue({ // 不需要注册,就能调用全局组件 el: "#app" }) </script>
4、父组件传递数据给子组件
- 通过绑定属性的方式进行数据传递
<!-- 1、根组件的标签位置内,调用子组件时,它不是一个真正的标签,知识作为一个占位,html页面上真正的标签时子组件调用后产生的真正的标签 2、由于不是真正的标签,因此它不具有标签默认属性,如style,class,name等等,包括事件也不能用 3、虽然不具有默认属性,但是可以自定义属性,包括自定义事件 4、自定义的属性名需要在组件内的成员props列表中以字符串的形式声明 5、自定义属性的属性值可以是父标签的变量,也可以是常量,在子组件内,通过反射的方式利用属性名获取属性值,即实现了父标签传递数据给子标签 --> <div id="app"> <global-tag :sup_data1='sup_data1' :supData2='sup_data2'></global-tag> </div> <script type="text/javascript"> Vue.component('global-tag', { props:['sup_data1', 'supdata2'], // 子组件内通过反射获取字符串对应属性值,不区分大小写 template: '<div>{{ sup_data1 }} {{ supdata2 }}</div>' }) new Vue({ el: '#app', data: { sup_data1: '数据1', sup_data2: '数据2' } }) </script>
5、子组件传递数据给父组件
- 通过发送事件请求的方式进行数据传递
<!-- 1、已经知道调用子组件时,可以自定义属性,实现父组件向子组件的数据传递 2、也可以自定义事件,事件的触发只能在子组件内部,事件的回调函数在父组件内声明 3、事件的触发:this.$emit('自定义的事件名', *args),主动触发,args是回传给回调函数的,可以是子组件的变量,可以是常量 4、事件的回调函数:'回调函数名'(*args){},args为子组件内触发该自定义事件后,返回的数据,即实现了子组件向父组件的数据传递 --> <div id="app"> <global-tag @send_action='receiveAction'></global-tag> <!--调用子组件时,自定义事件--> </div> <script type="text/javascript"> Vue.component('global-tag', { data () { return { sub_data1: "数据1", sub_data2: '数据2' } }, template: '<div @click="clickAction">发生</div>', methods: { clickAction () { this.$emit('send_action', this.sub_data1, this.sub_data2) // 主动触发自定义的事件,并返回数据给自定义事件的回调函数 } } }) new Vue({ el: '#app', methods: { receiveAction (v1, v2) { // v1,v2为子组件触发事件时传递的数据 console.log(v1, v2) } } }) </script>
6、父子组件实现todoList
<div id="app"> <div> <input type="text" v-model="val"> <button type="button" @click="submitMsg">提交</button> </div> <ul> <!-- <li v-for="(v, i) in list" :key="i" @click="removeMsg(i)">{{ v }}</li> --> <todo-list v-for="(v, i) in list" :key="i" :v="v" :i="i" @delect_action="delect_action"></todo-list> </ul> </div> <script type="text/javascript"> Vue.component("todo-list", { template: "<li @click='delect_action'><span>第{{ i + 1 }}条: </span><span>{{ v }}</span></li>", props: ['v', 'i'], methods: { delect_action () { this.$emit("delect_action", this.i) } } }) new Vue({ el: "#app", data: { val: "", list: [] }, methods: { submitMsg () { // 往list中添加input框中的value if (this.val) { this.list.push(this.val); this.val = "" } }, delect_action(index) { this.list.splice(index, 1) } } }) </script>
六、Vue-CLI 项目搭建
1、环境搭建
- 安装node
官网下载安装包,傻瓜式安装:https://nodejs.org/zh-cn/
- 安装cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
- 安装脚手架
cnpm install -g @vue/cli
- 清空缓存处理
npm cache clean --force // 当前面步骤出问题时,尝试此操作
2、项目的创建
- 创建项目
vue creat 项目名 // 要提前进入目标目录(项目应该创建在哪个目录下) // 选择自定义方式创建项目,选取Router, Vuex,babel插件 空格选择,enter确定
- 启动/停止项目
npm run serve / ctrl+c // 要提前进入项目根目录
- 打包项目
npm run build // 要在项目根目录下进行打包操作
3、认识项目
- 项目目录
dist: 打包的项目目录(打包后会生成) node_modules: 项目依赖 public: 共用资源 src: 项目目标,书写代码的地方 -- assets:资源 -- components:组件 -- views:视图组件 -- App.vue:根组件 -- main.js: 入口js -- router.js: 路由文件 -- store.js: 状态库文件 vue.config.js: 项目配置文件(没有可以自己新建)
- 配置文件:vue.config.js
module.exports={ devServer: { port: 8888 } } // 修改端口,选做
- main.js
// 这是项目的入口文件 // 加载项目环境,如Vue环境,router路由环境,store代码库环境 // render,由系统启动,将根组件APP作为参数,解析成html替换跟组件的挂载点 // 挂载el为"#app" // 加载环境只需要from即可,使用import得到的名字,可以在后面继续使用 import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' Vue.config.productionTip = false; // 新建项目tips new Vue({ router, store, render: h => h(App) //箭头函数, }).$mount('#app'); // 组件的声明周期,钩子函数mount,渲染标签成html
- .vue文件
<!-- 1、views文件夹存放页面组件,一般作为根组件的子组件 2、components文件夹存放小组件,一般作为页面组件的子组件 3、在使用组件的地方,要导入组件,并注册组件 --> <template> <!-- 模板区域 --> </template> <script> // 逻辑代码区域 // 该语法和script绑定出现 export default { // 使用export default语法,该组件才能被导出调用 } </script> <style scoped> /* 样式区域 */ /* scoped表示这里的样式只适用于组件内部, scoped与style绑定出现 */ </style>
4、项目功能
- vue-router
{ path: '/', name: 'home', // 路由的重定向 redirect: '/home' } { // 一级路由, 在根组件中被渲染, 替换根组件的<router-view/>标签 path: '/one-view', name: 'one', component: () => import('./views/OneView.vue') } { // 多级路由, 在根组件中被渲染, 替换根组件的<router-view/>标签 path: '/one-view/one-detail', component: () => import('./views/OneDetail.vue'), // 子路由, 在所属路由指向的组件中被渲染, 替换该组件(OneDetail)的<router-view/>标签 children: [{ path: 'show', component: () => import('./components/OneShow.vue') }] }
<!-- router-link渲染为a标签 --> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> | <router-link :to="{name: 'one'}">One</router-link> | <!-- 为路由渲染的组件占位 --> <router-view />
/*激活的router-link标签*/ a.router-link-exact-active { color: #42b983; }
// router的逻辑转跳:push({}) this.$router.push('/one-view') // 带参数的逻辑跳转 this.$router.push({name: '路由名', query: {拼接的参数}}) // router采用history方式访问上一级 this.$router.go(-1)
- vuex
// 在任何一个组件中,均可以通过this.$store.state.msg访问msg的数据 // state永远只能拥有一种状态值 state: { msg: "状态管理器" }, // 让state拥有多个状态值 mutations: { // 在一个一个组件中,均可以通过this.$store.commit('setMsg', new_msg)来修改state中的msg setMsg(state, new_msg) { state.msg = new_msg } }, // 让mutations拥有多个状态值 actions: { }
- vue-cookie
// 安装cookie的命令 // npm install vue-cookie --save // 为项目配置全局vue-cookie import VueCookie from 'vue-cookie' // 将插件设置给Vue原型,作为全局的属性,在任何地方都可以通过this.$cookie进行访问 Vue.prototype.$cookie = VueCookie
// 持久化存储val的值到cookie中 this.$cookie.set('val', this.val) // 获取cookie中val字段值 this.$cookie.get('val')
- axios
// 安装 axios(ajax)的命令 // npm install axios--save // 为项目配置全局axios import Axios from 'axios' Vue.prototype.$ajax = Axios
let _this = this this.$ajax({ method: 'post', url: 'http://127.0.0.1:5000/loginAction', params: { usr: this.usr, ps: this.ps } }).then(function(res) { // this代表的是回调then这个方法的调用者(axios插件),也就是发生了this的重指向 // 要更新页面的title变量,title属于vue实例 // res为回调的对象,该对象的data属性就是后台返回的数据 _this.title = res.data }).catch(function(err) { window.console.log(err) })
# 用pycharm启动该文件模拟后台 from flask import Flask, request, render_template from flask_cors import CORS app = Flask(__name__) CORS(app, supports_credentials=True) @app.route('/') def index(): return "<h1>主页</h1>" @app.route('/loginAction', methods=['GET', 'POST']) def test_action(): # print(request.args) # print(request.form) # print(request.values) usr = request.args['usr'] ps = request.args['ps'] if usr != 'abc' or ps != '123': return 'login failed' return 'login success' if __name__ == '__main__': app.run()
5、创建vue项目的流程
1、在main.js文件内加载项目环境和解析根组件,并渲染到网页
/* 1 加载vue、router、store环境 2 导入根组件APP 3 render解析根组件并渲染到网页 */ import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' Vue.config.productionTip = false; new Vue({ router, store, render: h => h(App) }).$mount('#app');
2、在根组件内加载页面组件
/* 1 加载页面组件的方式: <router-link to="页面组件的路由">Home</router-link> 2 被加载的页面组件,需要在router文件夹下的index.js文件中注册路由,在1中通过to="页面组件的路由"的方式实现页面组件的路由跳转 3 页面组件和小组件都需要使用导出语句定义实例,才能被其他文件导入,如下 */ export default { name: 'home', components: { HelloWorld, } }
3、页面组件的路由注册,在index.js中完成
/* 1 导入页面组件 2 在const routes中注册页面路由 3 注册方式: { path: '路由', name: '路由名', component: 页面组件名 },示例如下js代码 */ { path: '/', name: 'home', component: Home },
4、在页面组件内调用标签组件
/* 1 导入要调用的标签组件 2 在本页面组件的定义内,注册所有要用的小组件 */ // 导入组件 import HelloWorld from '@/components/HelloWorld.vue' export default { name: 'home', components: { HelloWorld, // 注册 } }
5、小组件的定义
/* 1 同样的组件定义方式,export语法 2 props成员,可以使用列表的形式,也可以使用如下的形式 根据属性值的类型,以键值对的方式,键为属性名,值为值数据类型 props:{ msg:String, info: Array, info1: Object, info2: JSON, date: Date, } */
相关推荐
Symiac 2020-07-04
Symiac 2020-07-04
颤抖吧腿子 2020-09-04
anaction 2020-08-17
bowean 2020-07-05
kiven 2020-09-11
softwear 2020-08-21
yuzhu 2020-11-16
85477104 2020-11-17
KANSYOUKYOU 2020-11-16
sjcheck 2020-11-03
怪我瞎 2020-10-28
源码zanqunet 2020-10-28
gloria0 2020-10-26
王军强 2020-10-21
学习web前端 2020-09-28
QiaoranC 2020-09-25
anchongnanzi 2020-09-21
安卓猴 2020-09-12