Vue之组件
vue的核心就是组件的使用,玩好了组件才能将前面的基础更好的运用起来。
组件使我们的项目解耦,更加符合vue的设计思想MVVM。
组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用小型、独立和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
例如,你可能会有页头,侧边栏,内容区等组件,每个组件又包含了其它的像导航栏链接、博文之类的组件
组件的创建
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <title>组件</title> </head> <body> <div id="app"></div> <script src="vue.js"></script> <script> // Vheader是组件名,最好大写开头 Vue.component('Vheader', {
// 和实例对象不一样,这里data不再返回对象,必须要是个函数,而且必须return data: function () { return { // 必须要return,哪怕是空对象 } }, template: `<div class="header"> <div class="w"> <div class="w1"> <img src="./logo.png"> </div> <div class="w-r"> <button>登陆</button><button>注册</button> </div> </div> </div>` }); var app = new Vue({ el: '#app', data: {}, computed: {}, methods: {} }) </script> </body> </html>
组件的使用
组件是可服用的Vue实例,并且带有一个名字,这个例子中是<Vheader> 。
我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:
<div id="app"> <Vheader></Vheader> </div>
组件的复用
<div id="app"> <Vheader></Vheader> <Vheader></Vheader> <Vheader></Vheader> </div>
vue-cli脚手架安装和webpack-simple模板项目生成
vue-cli是一个官方发布vue.js项目脚手架,使用vue-cli可以快速创建vue项目。
安装nodejs
官网下载安装
node -v检查是否安装成功
npm 是 nodejs package manager ,跟python的pip java mvn一样
安装vue-cli
安装好node之后,我们就可以直接全局安装vue-cli:
npm install -g vue-cli
安装慢的话用淘宝镜像,百度淘宝镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
如果安装失败使用npm cache clean清理缓存,然后再重新安装。
安装完成后vue -V查看vue版本。
vue hlep查看可用命令
vue list查看官方提供的模板
安装webpack-simple模板
初始化项目,加项目名myProject就创建一个项目名的目录里生成,不加名字默认再当前目录生成项目
vue init webpack-simple myProject
最后一步选N,sass是vue的预处理语言还不会。
然后它就告诉你要继续干什么:
webpack + nodejs 使我们更方便的开发,热重载。
使用的webpack模板
打开项目myProject,映入眼帘的是一个也看不懂。。。
等等..main.js:
import Vue from 'vue' import App from './App.vue' new Vue({ el: '#app', render: h => h(App) })
可以看出,这个和django的manage.py文件一样都是一个入口文件。
入口文件引的第一个文件就是App.vue:
看看人家是怎么设计的:
<!--一个组件有三部分组成--> <!-- 一个页面就把三个部分全部包含了 --> <template> <!--第一部分,页面的结构--> <!-- 注意template下整个必须是一个闭合标签,因为他是一个大组件包裹一个个小组件 --> <!-- 比如把下面的注释去掉就会报错 --> <!-- <div class="app2"></div> --> <div class="app"> <h3>{{ msg }}</h3> <p>heihei</p> </div> </template> <script> // 第二部分,页面的业务逻辑 export default { name:'App', // 只是个名字 data(){ // 数据属性必须是个函数 return { msg:'组件' } }, methods: { }, computed: { } } </script> <style> /*第三部分,css样式*/ </style>
解耦组件
上面的是一个大组件,要解耦成一个个小组件,所以我们要创建一个存放一个个小组件的文件加。
src下面新建一个folder,就叫components。
并新建三个文件:
每个文件里的结构,跟App.vue里的一样。
<template> <header class="nav"> <!--footer的class:foot,content:wrap--> 我是header </header> </template> <script> export default { name:'Vheader',// footer:Vfooter,content:content data(){ return { } }, } </script> <style scoped> //scoped:组件里的样式只在自己的组件里生效 </style>
引入子组件
接下来这些小组件怎么在大组件里用呢:
<template> <div class="app"> <h3>{{ msg }}</h3> <p>heihei</p> <Vcontent></Vcontent> <Vcontent></Vcontent> <Vcontent></Vcontent> </div> </template> <script> // 1. 先引入子组件 import Vcontent from './components/Vcontent.vue' import Vheader from './components/Vheader.vue' import Vfooter from './components/Vfooter.vue' export default { name:'App', data(){ return { msg:'组件' } }, methods: { }, computed: { }, // 2. 挂载组件 components:{ Vheader, // 相当于Vheader:Vheader,key和value相同可以简写 Vcontent, Vfooter, } } </script> <style> </style>
组件间传值
父组件向子组件传递数据,通过Prop
1. 父组件向子组件传值第一步就是, 绑定自定义的属性,在导入的子组件标签: <Vheader :title = '父组件中data声明的数据属性'/>
2. 父子传值的第二步就是,要通过props验证,再子组件里写这个方法
// 数组类型就必须写Array,写Str就会报错,对应类型可以查官网,然后就可以在子组件里用这个数据量了 props:{ cityArray:Array, }
type
可以是下列原生构造函数中的一个:
-
String
Number
Boolean
Array
Object
Date
Function
Symbol
子组件传递数据到父组件,通过自定义事件
1. 子父组件传值,第一步就是自定义事件
<Vcontent v-on:addCunHandler = 'addHandler'></Vcontent>
2. 给子组件中的某个按钮绑定原生事件,我们可以调用内建的 this.$emit('自定义的事件名','传递的数据'),来向父级组件触发一个自定义的事件.
methods:{ addCunHandler(){ // 通过$emit触发自定义事件 // 参数1:自定义事件的名字 // 参数2:数据 this.$emit('addCunHandler','北海北'); } },
MarkDown项目
写一个markdown项目,练习一下组件传值
父组件:App.vue
<template> <div class="app"> <Vheader></Vheader> <h2>{{ currentMsg }}</h2> <!-- webpack里的file-loader工具能把图片看做变量 --> <!-- 所以取图片用下面这种方式即可 --> <img :src="imgSrc"> <ul> <li v-for = 'item in getArray'> <!-- a标签里的href="javascript;"表示阻止a标签的事件,等于a标签失效了 --> <a href="javascript;">{{item}}</a> </li> </ul> <!-- 来个点击事件 --> <button @click='clickHandler'>修改</button> <!-- 子父组件传值,第一步就是自定义事件 --> <Vcontent v-on:addCunHandler = 'addHandler'></Vcontent> <!-- 父子组件传值,第一步就是绑定自定义属性 --> <Vfooter :cityArray = 'citys'></Vfooter>
</div> </template> <script> import imgSrc from './assets/logo.png' // 子组件渲染到父组件的第一步就是,导入 import Vheader from './components/Vheader.vue' import Vcontent from './components/Vcontent.vue' import Vfooter from './components/Vfooter.vue' export default { name:'App', data(){ return { msg:'hello world', stars:[ "nsn","alex",'nezha' ], imgSrc:imgSrc, citys:['沙河','通州','西二旗'] } }, methods: { clickHandler(){ this.msg = '哈哈哈', this.stars.push('anglebaby') }, addHandler(str){ this.citys.push(str) } }, // 所有的属性,都可以被计算属性绑定起来,多用计算属性 computed: { currentMsg(){ return this.msg; }, getArray(){ return this.stars } }, // 子组件渲染到父组件的第二步就是,挂载 components:{ Vheader, Vfooter, Vcontent } } </script> <style> *{ padding: 0px; margin: 0px; } </style>
子组件Vfooter
<template> <footer class="foot"> <ul v-for = 'item in cityArray'> <li>{{item}}</li> </ul> </footer> </template> <script> export default { name:'Vfooter', data(){ return { } }, // 父子传值的第二步就是,一定要通过props验证 // 数组类型就必须写Array,写Str就会报错,对应类型可以查官网,然后就可以在上面用了 props:{ cityArray:Array, } } </script> <style> </style>
子组件Vcontent
<template> <div class="wrap"> <div class="t"> <button @click='addCunHandler'>添加一个村庄</button> </div> <div class="mark"> <textarea rows="10" cols="100" class="editor" v-model = 'markValue'></textarea> <div class="show" v-html = 'markedValue'></div> </div> </div> </template> <script> // npm install marked --save,别人写好的markdown工具,直接解析你的markdown语法 import Marked from 'marked' export default { name:'Vcontent', data(){ return { markValue:'' } }, methods:{ // 子组件向父组件传值,第二步就是触发自定义事件 addCunHandler(){ // 通过$emit触发自定义事件 // 参数1:自定义事件的名字 // 参数2:数据 this.$emit('addCunHandler','北海北'); } }, computed:{ markedValue(){ return Marked(this.markValue) } } } </script> <style scoped> .t { width: 300px; height: 100px; } .mark{ width:1210px; height: 600px; margin: 0 auto; } .editor,.show{ float: left; width: 603px; height: 600px; border: 1px solid #666; } </style>
子组件Vheader
<template> <header class="header"> <div class="nav"> <div class="logo"> <img :src="imgSrc" /> </div> <div class="info"> <button>注册</button> <button>登陆</button> </div> </div> </header> </template> <script> import imgSrc from '../assets/logo.png' export default { name:'Vheader', data(){ return { imgSrc:imgSrc } }, } </script> <style scoped> .header{ width: 100%; height: 40px; background-color: #fff; box-shadow: 0 2px 4px 0 #c9c9c9; /* box-shadow是框边的阴影,更有立体感*/ } .header .nav{ width: 980px; height: 40px; margin: 0 auto; /*transparent透明*/ background-color: transparent; } .nav .logo{ width: 100px; height: 40px; float: left; } .logo img{ width: 40px; height: 40px; } .nav .info{ float: right; width: 200px; height: 40px; } .info button{ width: 80px; height: 40px; float: left; } </style>