# vue_组件化编码
# 一、使用vue-cli创建模板项目
# 1】vue-cli是vue官方提供的脚手架工具(一个库)
http://www.github.com/vuejs/vue-cli
# 2】创建vue项目
npm install -g vue-cli(注:只需要安装一次脚手架)
vue init webpack vuedemo(注:webpack是模板,提供了6个可选模板项目)
(会提示几个问题,第一个问题name必须小写,暂时不需要安装vue-router,两个单元测试的包也不需要)
cd vuedemo
npm install
npm run dev
访问:http://localhost:8080/
# 二、基于脚手架编写项目
# 1】组件之.vue格式文件
//文件名格式为App.vue
<template></template>//写html
<script></script>//写js
<style></style>//写css
# 2】基本步骤
①在src文件夹新建一个根组件,名为:App.vue
<!--根组件-->
<template>
<div>
<img class="logo" src="./assets/logo.png" alt="logo">
<!--3.使用组件标签-->
<HelloWorld></HelloWorld>
</div>
</template>
<script>
//1.引入组件
import HelloWorld from "./components/HelloWorld";
export default {
//2.映射组件标签
comments:{
HelloWorld
}
}
</script>
<style>
.logo{
width: 200px;
height: 200px;
}
</style>
②在src文件夹下新建components文件夹,然后再该文件夹下新建一个子组件,名为:HelloWorld.vue
<!--子组件-->
<template>
<div>
<p class="msg">{{msg}}</p>
</div>
</template>
<script>
export default {//配置对象(与Vue一致)
data(){//data必须是一个函数,否则在用相同的组件时,组件之间会公用一个数据集
//用函数的话,用一次组件时就会生成一个实例,然后data()返回的数据,占用的内存地址是不一样的
return {msg:'helloworld'}
}
}
</script>
<style>
.msg{
color: red;
font-size: 30px;
}
</style>
③在src文件夹中还需要新建一个main.js文件
/*
入口JS:创建Vue实例
*/
import Vue from 'vue'
import App from "./App"
new Vue({
el:'#app', //是根据index.html里面的div的id值
components:{
App
},
template:'<App/>' //模板:vue生命周期中有定义
})
# 三、项目的打包与发布
# 1】打包:
npm run build
# 2】发布1:使用静态服务器工具包
npm install -g serve
serve dist (会生产一个新文件夹dist)
访问:http://localhost:5000
# 3】发布2:使用动态web服务器(tomcat)
①修改配置:webpack.prod.conf.js
output:{
publicPath:'/xxx/' //打包文件夹名称,要发布的项目名称
}
//如果又要使用静态服务器发布记得需要还原这步,重新打包
②重新打包:
npm run build
③修改dist文件夹为项目名称:xxx
④将xxx拷贝到运行的Tomcat的webapps目录下
⑤访问:http://localhost:8080/xxx
# 四、ESLint编码规范检查
1】ESLint是一个代码规范检查工具
2】它定义了很多特定的规则,一旦你的代码违背了某一规则,ESLint会作出非常有用的提示
3】官网:http://eslint.org/
4】基本已替代以前的JSLint
# 五、注册组件的基本步骤(Vue2.x以前)
# ①创建组件构造器(调用Vue.extend()方法创建组件构造器)
含义:
1.调用Vue.extend()创建的是一个组件构造器
2.通常在创建组件构造器时,传入template代表我们自定义组件的模板
3.该模板就是在使用到组件的地方,要显示的HTML代码
4.也可以在这里面用components:{标签名:构造器名}注册子组件
<script>
//1.创建组件构造器
const myComponent=Vue.extend({
template:`
<div>
<h2>组件标题</h2>
</div>`
});
</script>
# ②注册组件(调用Vue.component()方法注册全局组件)
含义:
1.调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称
2.所以需要传递两个参数:①注册组件的标签名②组件构造器
<script>
//2.注册组件,并且定义组件标签的名称
Vue.component('my-cpn',myComponent)
</script>
# ③使用组件(在Vue实例的作用范围内使用组件)
组件必须挂载在某个Vue实例下,否则它不会生效
<div>
<!--3.使用组件-->
<my-cpn></my-cpn>
</div>
# 六、Vue2.0以后推荐使用注册组件语法糖
# 1】Vue为了简化过程,提供了注册的语法糖,主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。
# 2】不再需要写Vue.extend()组件构造器,Vue优化了。只需要把Vue.component('',{template:....})来使用。
<script>
//全局组件注册语法糖
Vue.component('cpn1',{
template:`
<div>
<h2>hhhh</h2>
</div>`
})
//局部组件注册语法糖
new Vue({
el:'#demo',
data:{
message:'你好啊'
},
components:{
'cpn2':{template:`
<div>
<h2>hhhh</h2>
</div>`
}
}
})
</script>
# 七、组件模板抽离的写法
# 1】script标签,注意:类型必须是text/x-template
<script type="text/x-template" id="cpn">
<div>
<h2>hhhh</h2>
</div>
</script>
# 2】template标签
<template id="cpn">
<div>
<h2>hhhh</h2>
</div>
</template>
# 八、组件不可以访问Vue实例数据
# 1】组件是一个单独功能模块的封装:
这个模块有属于自己的HTML模板,也应该有属于自己的数据data。
# 2】组件不能直接访问Vue实例中的data
组件可以存放自己的数据,但只能用data(){return{}}
①组件对象也有一个data属性
②只是这个data属性必须是一个函数
③而且这个函数返回一个对象,对象内部保存着数据
# 九、父子组件通信---父传子props(properties属性)
# 1】props的值有两种方式:
①方式一:字符串数组,数组中的字符串就是传递时的名称。
②方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
# 2】props数据验证,有很多种写法(了解)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<cpn :cmovie="movie" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div >
<ul>
<li v-for="item in cmovie" :key="index">{{item}}</li>
</ul>
<p>{{cmovie}}</p>
<h2>{{cmessage}}</h2>
</div>
</template>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
const cpn={
template:'#cpn',
// props:['cmovie','cmessage']
props:{
//1.类型限制
cmovie:Array,
cmessage:String
//2.提供一些默认值,提供一些默认值,限制在标签中必须给变量传值
/* cmessage:{
type:String,
default:'aaaa',
required:true
},
//当类型是对象或者数组时,默认值必须是一个函数返回一个对象(或数组)
cmovie:{
type:Array,
default(){
return []
}
}
*/
}
}
const app=new Vue({
el:'#app',
data:{
message:'你好啊',
movie:['海王 ','海贼王']
},
components:{
'cpn':cpn
}
})
</script>
</body>
</html>
# 3】父传子,props中的驼峰标识
①现版本已经支持驼峰
②在template标签中,必须要用div标签包裹其他元素
# 十、父子组件通信---子传父(自定义事件)
# 1】当子组件需要向父组件传递数据时,就需要用到自定义事件了。
# 2】我们之前学的v-on:(@)不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
# 3】自定义事件的流程:
①在子组件中,通过$emit(eventName,[args1,arg2..])来触发事件。(触发当前实例上的事件。附加参数都会传给监听器回调。)
②在父组件中,通过v-on来监听子组件事件。
# 4】我们来看一个例子:
①我们之前做过一个两个按钮+1和-1,点击后修改counter。
②我们整个操作的过程还是在子组件中完成,但是之后的展示交给父组件。
③这样,我们就需要将子组件中的counter,传给父组件的某个属性,比如total。
<div id="app">
<cpn @addclick="add" @incclick="inc"></cpn>
</div>
<template id="cpn">
<div>
<button @click="addclick">+</button>
<button @click="incclick">-</button>
</div>
</template>
<script type="text/javascript" src="img/vue.js"></script>
<script type="text/javascript">
const cpn={
template:`#cpn`,
data(){
return{
num:0
}
},
methods:{
addclick(){
this.$emit('addclick',this.num++)
},
incclick(){
this.$emit('incclick',this.num--)
}
}
}
const app=new Vue({
el:'#app',
data:{
},
components:{
cpn
},
methods:{
add(num){
console.log(num)
},
inc(num){
console.log(num)
}
}
})
</script>
# 十一、父访问子-children-refs($refs很重要)
1】有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件
# ①父组件访问子组件:使用$children或$refs
$children 把所有子组件存在数组中,通过数组下标进行取用不同的子组件(this.$children[x].子组件内容)
$refs 默认是空对象,通过在子组件标签里面加属性ref="xxx",(this.$refs.xxx.子组件内容),来取用不同的子组件
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="aaa"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
const app=new Vue({
el:'#app',
data:{
message:'nihaoa'
},
methods:{
btnClick(){
// 1.$children
// console.log(this.$children)//把每个子组件放在数组里面
// for (let c of this.$children){
// console.log(c.name)
// c.showMessage();
// }
// console.log(this.$children[2].name)
// 2.$refs => 对象类型,默认是空对象,在标签加ref="aaa"
console.log(this.$refs.aaa.name)
}
},
components:{
cpn:{
template:'#cpn',
data(){
return{
name:'我是子组件的name'
}
},
methods:{
showMessage(){
console.log('showMessage')
}
}
}
}
})
</script>
# ②子组件访问父组件或者根组件:使用$parent或$root
$parent 取父组件的方法或者数据(用的很很少) this.$root.xxx
$root 取根组件的方法或者数据(用的很少) this.$parent.xxx
<div id="app">
<cpn></cpn>
</div>
<template id="ccpn">
<div>
<h2>我是子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<template id="cpn">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</template>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
const app=new Vue({
el:'#app',
data:{
message:"你好啊"
},
components:{
cpn:{
template:'#cpn',
data(){
return{
name:'我是cpn组件的name'
}
},
components:{
ccpn:{
template:'#ccpn',
methods:{
btnClick(){
//1.访问父组件$parent(用的很少)
console.log(this.$parent)
console.log(this.$parent.name)
//2.访问根组件$root,直接访问Vue实例
console.log(this.$root)
console.log(this.$root.message)
}
}
}
}
}
}
})
</script>
# 十二、插槽slot
# 1】组件的插槽:
①组件的插槽也是为了让我们封装的组件更加具有扩展性。
②让使用着可以决定组件内部的一些内容到底展示什么。
③预备插槽,以备不时之需
# 2】插槽的基本使用:
# 1、在组件里面定义
<slot></slot>
# 2、插槽的默认值
<slot><button></button></slot>
# 3、如果有多个值,同时放入到组件进行替换时,一起作为替换元素
<div id="app">
<cpn></cpn><!--按钮-->
<cpn><span>哈哈哈</span></cpn><!--span标签-->
<cpn><i>呵呵呵</i></cpn><!--i标签-->
<cpn></cpn><!--按钮-->
<cpn><!--多种元素一起替换-->
<h1>嘿嘿嘿</h1>
<div>嘿嘿嘿</div>
<p>嘿嘿嘿</p>
</cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>我是组件,哈哈哈</p>
<slot><button>按钮</button></slot>
</div>
</template>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
const app=new Vue({
el:'#app',
data:{
message:'你好啊'
},
components:{
cpn:{
template:'#cpn'
}
}
})
</script>
# 十三、具名插槽slot
1】当子组件的功能复杂时,子组件的插槽可能并非是一个。
①比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表右边、中间、右边。
②那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?
③这时候,我们就需要给插槽起一个名字
2】如何使用具名插槽呢?
①非常简单,只要给slot元素一个name属性即可
②
3】我们给出了一个案例:
<div id="app">
<cpn><span>替换hhh</span></cpn>
<cpn><span slot="center">标题</span></cpn>
<cpn><button slot="left">返回</button></cpn>
</div>
<template id="cpn">
<div>
<slot name="left"><span>左边</span></slot>
<slot name="center"><span>中间</span></slot>
<slot name="right"><span>右边</span></slot>
<slot>hhh</slot>
</div>
</template>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
const app=new Vue({
el:'#app',
data:{
message:'你好啊'
},
components:{
cpn:{
template:'#cpn'
}
}
})
</script>
# 十四、编译作用域 与 作用域插槽
# 1】官方准则:
父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
<div id="app">
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
<div>
<h2 v-show="isShow">我是子组件</h2>
<p>我是内容,哈哈哈</p>
</div>
</template>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
const app=new Vue({
el:'#app',
data:{
message:'你好啊',
isShow:true
},
components:{
cpn:{
template:'#cpn',
data(){
return{
isShow:false
}
}
}
}
})
</script>
# 2】作用域插槽(有点难)
①父组件替换插槽的标签,但是内容由子组件来提供。
(就是父组件对子组件展示数据的方式不满意,父组件需要从子组件中拿到子组件中的数据,再用插槽作用域拿到数据进行重新展示)
<div id="app">
<cpn></cpn>
<cpn>
<template v-slot="slot">
<!--也可以用slot-scope-->
<!--<span v-for="item in slot.abc">{{item}} - </span>-->
<span>{{slot.abc.join(' - ')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<slot :abc="Planguage">
<ul>
<li v-for="item in Planguage">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
const app=new Vue({
el:'#app',
data:{
message:'你好啊',
},
components:{
cpn:{
template:'#cpn',
data(){
return{
Planguage:['JavaScript','C++','Java','C#','Python']
}
},
created(){
this.Planguage.join()
}
}
}
})
</script>