一、组件化编程
使用组件编程至少需要三步:
- 定义组件
- 注册组件
- 使用组件
1.1 全局注册
<body>
<div id="app">
<!-- 3.使用组件 -->
<abc></abc>
<abc></abc>
<abc></abc>
</div>
<!-- 1.定义组件:在组件中如果存在多个标签,那么这些标签应该在同一个根标签中 -->
<template id="temp">
<div>
<img src="1.png" alt="">
<p>功能菜单名字</p>
</div>
</template>
<script>
// 2.注册组件 - 全局注册
//参数1:组件的名字 参数2:将哪个template注册成组件
Vue.component("abc",{
template:"#temp"
});
new Vue({
el:"#app"
});
</script>
</body>
1.2 局部注册
<body>
<div id="app">
<abc></abc>
<abc2></abc2>
</div>
<!-- 1.定义组件:在组件中如果存在多个标签,那么这些标签应该在同一个根标签中 -->
<template id="temp1">
<div>
<img src="1.png" alt="">
<p>功能菜单名字</p>
</div>
</template>
<template id="temp2">
<div>
<img src="1.png" alt="">
<p>功能菜单名字2</p>
</div>
</template>
<script>
//2.创建组件
const abc = {
template : "#temp1"
}
const abc2 = {
template : "#temp2"
}
new Vue({
el:"#app",
//3.注册组件 - 局部注册
components:{
abc,
abc2
}
});
</script>
</body>
1.3 子组件获取数据
<body>
<div id="app">
<!-- 3.使用组件 -->
<goods></goods>
<goods></goods>
</div>
<!-- 1.定义组件 -->
<!-- 重点:1.组件默认情况下永远都是获取到本组件的属性值
2.在子组件中定义数据,必须放在data()方法中,而且必须返回一个对象{key:value,key:value}
-->
<template id="temp">
<div>
<img :src="url" alt="">
<p>{{name}}</p>
<input type="text" v-model="name"><br>
</div>
</template>
<script>
// 2.注册组件
Vue.component("goods",{
template : "#temp",
// 在子组件中data必须是函数、方法,不能是属性,数据存放在return的对象中
data(){
// 返回的永远是一个新的对象
return {
url:"1.png",
name:"摇曳露营"
}
}
});
new Vue({
el:"#app"
});
</script>
</body>
注意:**组件默认情况下永远都是获取到本组件中的属性值;在子组件中定义数据,必须放在data()方法中,而且必须要返回一个对象{key:value,key:value}**
1.4 父子组件之间的通信
1.4.1 父传子
在子组件中通过props指定属性,然后再通过v-bind给其绑定值
<body>
<div id="app">
<goods v-bind:cgoods="goods" v-for="goods in goodsList"></goods>
</div>
<template id="temp">
<div>
<img :src="cgoods.url" alt="">
<p>{{cgoods.name}}</p>
<input type="text" v-model="cgoods.name"><br>
</div>
</template>
<script>
Vue.component("goods",{
template : "#temp",
props:{
cgoods : {
//设置默认值,如果没有传值才会显示
default:{url:"1.png",name:"摇曳露营-默认"}
}
}
});
new Vue({
el:"#app",
data:{
goodsList:[
{url:"1.png",name:"摇曳露营1"},
{url:"1.png",name:"摇曳露营2"},
{url:"1.png",name:"摇曳露营3"}
]
}
});
</script>
</body>
注意:如果父组件传进来多个数组,props要用数组接收
例如:
**
父组件:
<!-- 使用子组件 -->
<activitydetail :activity="currentActivity"
:userData="currentUsers">
</activitydetail>
子组件:
<script>
export default {
props: [
//父组件传入多个数据用数组接收
'activity',
'userData'
],
}
</script>
1.4.2 子传父
通过自定义事件实现数据传递
在子组件中给click事件绑定方法getData(),并在该方法中通过this.$emit()方法发出事件、传递数据
<body>
<div id="app">
<!-- 自定义事件绑定的方法,如果需要获取到子组件传递的数据,那么不要写(),
如果写了在vue看来该方法是无参方法,数据就不会被获取到 -->
<goods v-bind:cgoods="goods" v-for="goods in goodsList" @abc="childClick"></goods>
</div>
<template id="temp">
<div>
<img :src="cgoods.url" alt="" @click="getData()">
<p>{{cgoods.name}}</p>
<input type="text" v-model="cgoods.name"><br>
</div>
</template>
<script>
Vue.component("goods",{
template : "#temp",
props:{
cgoods : {
//设置默认值,如果没有传值则显示
default:{id:1000,url:"1.png",name:"摇曳露营-默认"}
}
},
methods:{
getData(){
//将id传给父组件:自定义事件 $emit用来发出事件并且可以传出数据->父组件
this.$emit("abc",this.cgoods.id);
}
}
});
new Vue({
el:"#app",
data:{
goodsList:[
{id:1001,url:"1.png",name:"摇曳露营1"},
{id:1002,url:"1.png",name:"摇曳露营2"},
{id:1003,url:"1.png",name:"摇曳露营3"}
]
},
methods:{
childClick(id){
console.log(id);
}
}
});
</script>
</body>
- 在app div中添加事件监听,并调用vue对象中的childClick方法
注意:**自定义事件绑定的方法,如果需要获取到子组件传递的数据,那么不要写(),如果写了在vue看来该方法是一个无参方法,数据就不会被获取到
例如:**
<goods v-bind:cgoods="goods" v-for="goods in goodsList" @abc="childClick"></goods>
执行逻辑:
1.5 插槽
作用:提升组件的扩展性。
很多时候组件之间的差异非常小,那么在代码层面就会出现很大的冗余,而且每个组件的标签都是不可变的,组件的扩展性就非常低,这些问题可以通过使用插槽来解决
1.5.1 匿名插槽(没有名字的插槽)
<body>
<div id="app">
<temp>
<!-- 指定标签替换插槽中的内容(包括标签) 默认去替换匿名插槽的内容 -->
<h4>父组件自己指定的显示数据的标签</h4>
</temp>
</div>
<template id="temp">
<div>
<h3>子组件</h3>
<!-- 匿名插槽 -->
<slot>
<p>要显示的内容</p>
</slot>
</div>
</template>
<script>
Vue.component("temp",{
template:"#temp"
});
new Vue({
el:"#app"
});
</script>
</body>
1.5.2 具名插槽(具有名字的插槽,精确替换)
<body>
<div id="app">
<temp>
<!-- 指定替换哪个插槽中的内容 -->
<h4 slot="abc">替换具名插槽中的内容</h4>
</temp>
</div>
<template id="temp">
<div>
<h3>子组件</h3>
<!-- 具名插槽 -->
<slot name="abc">
<p>具名插槽内容</p>
</slot>
</div>
</template>
<script>
Vue.component("temp",{
template:"#temp"
});
new Vue({
el:"#app"
});
</script>
</body>
1.5.3 作用域插槽(带数据的插槽)
<body>
<div id="app">
<temp>
<!-- 作用域插槽 -->
<template slot-scope="slot">
<p v-for="book in slot.data">{{book.name}}</p>
</template>
</temp>
</div>
<template id="temp">
<div>
<h3>子组件</h3>
<!-- 作用域插槽:带有数据的插槽 data属性自定义,名字可以随意取 此处是将books的值绑定到data属性上-->
<slot :data="books">
<ul>
<li v-for="book in books">{{book.name}}</li>
</ul>
</slot>
</div>
</template>
<script>
Vue.component("temp",{
template:"#temp",
data(){
return {
books:[
{name:"西游记"},
{name:"钢铁是怎样炼成的"},
{name:"Java从入门到放弃"}]
}
}
});
new Vue({
el:"#app"
});
</script>
</body>
执行逻辑
二、模块化编程
2.1 简单示例
order.js
let a = 10;
goods.js
let a = 20;
index.html
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 在导入js的时候告知是一个模块 在运行时需要运行在服务器上 安装live server插件-->
<script src="order.js"></script>
<script src="goods.js"></script>
<script>
console.log(a);
</script>
</head>
运行HTML页面会报错,a重复定义,原因是order.js和goods.js中都有名字叫a的变量
解决办法:通过模块化实现,步骤如下
order.js
let a = 10;
//导出
//可以通过给变量一个默认的名字 导入者可以自定义变量名 解决代码冲突
export default {
a
};
创建main.js导入order和goods模块中的变量
import aa from "./goods.js"
import aaa from "./order.js"
console.log(aa);//此处的aa就是goods中的a
console.log(aaa);//此处的aaa就是order中的a
在index.html引入这三个js文件时指定为模块js
<!-- 在导入js的时候告知是一个模块 在运行时需要运行在服务器上 安装live server插件-->
<script src="order.js" type="module"></script>
<script src="goods.js" type="module"></script>
<script src="main.js" type="module"></script>
打开index.html运行,显示跨域错误,因为此时是用模块开发,需要使用服务器运行,而不能直接通过浏览器打开,所以此处安装”Live Server“插件,然后通过该插件运行
2.2 export导出
详解:可以导出单个变量、多个变量、属性、类
//单独导出某个变量
export let age = 10;
//一次性导出多个变量
let a = 10;
let b = 20;
let c = 30;
export {a,b,c}
//导出方法
export function say() {
console.log("hello");
}
//导出一个类
export class Teacher{
teaching(){
console.log("good good study");
}
}
main.js导入
import {age,a,b,c,say,Teacher} from "./export.js"
console.log(age);
console.log(c);
//调用方法
say();
//创建对象
let teacher = new Teacher();
teacher.teaching();//调用方法
2.3 import导入
如果需要导入的js文件中有多个export,一个一个导入非常麻烦,此时可以通过*一次性导入
import * as abc from "./export.js"
console.log(new abc.Teacher());
三、脚手架
node.js与vue脚手架安装见文档:脚手架安装.docx
3.1 搭建项目流程解析
vue init webpack 项目名
init 初始化
webpack vue的模板(框架)
project name 可以再次指定项目名字 此处直接回车
project description 项目描述 可以指定也可以直接回车
author 作者,项目创建人 直接回车 不需要配置
项目构建方式:此处选择第一个大多数人选择方式,这两种方式就代码上有些许的不一样
是否要使用vue的路由:此处选择n
是否使用语法校验器ESLint:此处选择n
是否使用单元测试:此处选择n
是否采用e2e(端到端)测试方式:此处也是n
以什么方式来管理项目中需要用到的包:此处采用npm方式,直接回车
创建完毕
3.2 项目结构说明
build webpack配置
config vue基本配置
node-modules vue依赖包
src 源代码
assets 静态资源(js、css)
components 组件
App.vue App组件(demo)
main.js 程序入口,加载组件
static 静态资源文件(图片、json数据)
index.html 首页
package.json 项目基本信息
3.3 项目结构解析
3.3.1 index.html
项目的首页
<div id="app"></div>
3.3.2 main.js
main.js是整个项目的入口js,最先被加载
- 创建Vue对象,作用于id为app的div标签上
- 注册主键:组件名叫做App
App通过import导入,从App.vue中导入
import Vue from 'vue' import App from './App' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', components: { App }, template: '<App/>' })
3.3.3 App.vue
是整个项目的主要组件,此组件主要包含三个部分(其实每一个vue文件都应该包含这三个部分)
template 定义组件
- script 导出组件、添加数据、添加当前组件事件方法、导入其他的组件
- style 指定当前组件中的标签的css
<template> <div id="app"> <img src="./assets/logo.png"> <HelloWorld/> </div> </template> <script> import HelloWorld from './components/HelloWorld' export default { name: 'App', components: { HelloWorld }, data(){ } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
3.3.4 自定义模块
1、在components目录下创建home.vue
2、在App.vue中导入home模块、注册、使用<template> <!-- 必须要有一个根 --> <div> <h2>这是我自己的组件</h2> <p>{{message}}</p> </div> </template> <script> export default { name:"abc", data(){ return { message:"hello vue" } } } </script> <style scoped> h2{ color: red; } </style>
<template> <div id="app"> <!-- 3.使用 --> <Home></Home> </div> </template> <script> // 1.导入 import Home from "./components/home" export default { name: 'App', components: { // 2.注册 Home } } </script> <style> </style>