本文已参与「新人创作礼」活动,一起开启掘金创作之路。
组件化
页面上小到一个按钮都可以是一个单独的文件.vue,这些小组件直接可以像乐高积木一样通过互相引用而组装起来
以element-ui的button组件示例,下图的每一个button都是一个单独的组件,以达到代码的最大化复用:
组件注册
全局注册
要注册一个全局组件,可以使用 Vue.component(tagName, options),注册在跟实例下。
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
局部注册
你不必把每个组件都注册到全局。你可以通过某个 Vue 实例/组件的实例选项 components 注册仅在其作用域中可用的组件。
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 将只在父组件模板中可用
'my-component': Child
}
})
封装组件
** 封装组件的三种方法: **
vue单页面组件
这种方法常用在vue文件中
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'hello',
data () {
return {
msg: '欢迎!'
}
}
}
</script>
script模板
<script type="text/x-template" id="myComponent">//注意 type 和id。
<div>This is a component!</div>
</script>
<script>
//全局注册组件
Vue.component('my-component',{
template: '#myComponent'
})
new Vue({
el: '#app'
})
</script>
html模板
<template id="myComponent">
<div>This is a component!</div>
</template>
<script>
Vue.component('my-component',{
template: '#myComponent'
})
new Vue({
el: '#app'
})
</script>
或者
<script>
var myComponent =
`<div>This is a component!</div>
<p>----Calamus</p>`;
Vue.component('my-component',{
template: myComponent
})
new Vue({
el: '#app'
})
</script>
开发组件
安装vue
- script标签导入
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
- vue-cli安装
# 全局安装 vue-cli
$ npm install --global vue-cli
# 创建一个基于 webpack 模板的新项目
$ vue init webpack my-project
注意:一些eslink e2e等工具是语法检查用的,建议最开始关闭,不然比较麻烦
# 安装依赖
$ cd my-project
$ npm install
# 运行
$ npm run dev
运行之后打开localhost:8080,即时vue安装好的页面了
# 打包编译
$ npm run build
打包成功之后项目目录下会多出dist文件夹,即是打包编译好的项目文件
注意:build之后出现页面空白,打开调试发现js、css等资源加载404,是webpack配置相对路径错误导致,如图路径给为 './'即可
注意:build之后出现font等字体文件加载错误,也是webpack配置问题,修改build->webpack.base.conf.js 里css-loader的limit值,比你的font文件大即可
- bower安装
$ bower install vue
开发一个canvas组件示例
<template>
<canvas id="canvas">
</canvas>
</template>
<script>
export default {
name:'CLCanvasBg',
props: {
//原点数量
dotsNum: {
type: Number,
default: 50
},
//彩色还是黑白
isColor: {
type: Boolean,
default: true
},
//圆的颜色
roundColor: {
type: String,
default: "#999"
},
//直线颜色
lineColor: {
type: String,
default: "#ccc"
}
},
mounted() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const rndCl = () => Math.floor(Math.random() * 225);
const width = window.innerWidth;
const height = window.innerHeight;
var base_list = [];
canvas.width = width;
canvas.height = height;
// 绘制园
const drawRounds = (obj, index) => {
let { x, y, r, color } = obj;
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI);
if (this.isColor) {
ctx.fillStyle = color;
} else {
ctx.fillStyle = this.roundColor
}
ctx.fill();
ctx.closePath();
}
//判断移动方向
const controlDirection = (obj) => {
if (obj.x >= (width - obj.r)) {
obj.controlX = "left";
} else if (obj.x <= parseInt(obj.r / 2)) {
obj.controlX = "right";
}
if (obj.y >= (height - obj.r)) {
obj.controlY = "bottom";
} else if (obj.y <= parseInt(obj.r / 2)) {
obj.controlY = "top"
}
return obj
}
//划线
const drawLine = (list) => {
list.map((item, index) => {
ctx.beginPath();
ctx.moveTo(item.x1, item.y1);
ctx.lineTo(item.x2, item.y2);
ctx.LineWeight = 1;
if (this.isColor) {
ctx.strokeStyle = item.color;
} else {
ctx.strokeStyle = this.lineColor
}
ctx.stroke();
ctx.closePath();
})
}
//绘制
const draw = (list) => {
let dots_arr = [];
let line_arr = [];
list.map((item, index) => {
let xy = controlDirection(item);
let obj = roundMove(xy);
dots_arr.push(obj);
});
dots_arr.map((item1, index1) => {
dots_arr.map((item2, index2) => {
if (item1 !== item2) {
let x = item1.x - item2.x;
let y = item1.y - item2.y;
if (Math.abs(x) < 150 && Math.abs(y) < 150) {
let obj = {
x1: item1.x,
y1: item1.y,
x2: item2.x,
y2: item2.y,
color: item1.color
}
line_arr.push(obj)
}
}
})
})
drawLine(line_arr);
dots_arr.map((item, index) => {
drawRounds(item, index)
})
base_list = dots_arr;
setTimeout(() => {
// if(this.paused){
reDraw()
// }
}, 50)
}
const reDraw = () => {
ctx.clearRect(0, 0, width, height);
draw(base_list)
}
//移动
const roundMove = (obj) => {
switch (obj.controlX) {
case "right":
obj.x++;
break;
case "left":
obj.x--;
break;
default:
}
switch (obj.controlY) {
case "top":
obj.y++;
break;
case "bottom":
obj.y--;
break;
default:
}
return obj
}
//创造圆点
const creatDots = () => {
let arr = [];
for (let i = 0; i < this.dotsNum; i++) {
let obj = {};
obj.x = parseInt(Math.random() * width);
obj.y = parseInt(Math.random() * height);
obj.r = parseInt(Math.random() * 10);
obj.controlX = parseInt(Math.random() * 10) > 5 ? "left" : "right"
obj.controlY = parseInt(Math.random() * 10) > 5 ? "bottom" : "top"
obj.color = `rgba(${rndCl()},${rndCl()},${rndCl()},.3)`
arr.push(obj)
}
return arr
}
draw(creatDots())
//鼠标移动
const moveXY = (event) => {
let obj = {};
obj.x = event.clientX;
obj.y = event.clientY;
obj.r = 0;
base_list[0] = obj;
base_list[0]["r"] = 1;
}
//鼠标点击
const addXY = (event) => {
let obj = {};
obj.x = event.clientX;
obj.y = event.clientY;
obj.r = parseInt(Math.random() * 10);
obj.color = `rgba(${rndCl()},${rndCl()},${rndCl()},.3)`;
obj.controlX = parseInt(Math.random() * 10) > 5 ? 'left' : 'right'
obj.controlY = parseInt(Math.random() * 10) > 5 ? 'bottom' : 'top'
base_list.push(obj);
}
window.addEventListener("mousemove", moveXY);
window.addEventListener("click", addXY)
},
}
</script>
<style>
#canvas{
position: fixed;
z-index: -1;
top: 0;
left: 0;
}
</style>
在线运行示例:
组件三要素
- prop
prop需要转化成对应的kebab-case (短横线分隔式命名)
组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop 才能下发到子组件中-
自定义事件
-
slot
<app>
<app-header></app-header>
<app-footer></app-footer>
</app>
为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个过程被称为内容分发 (即 Angular 用户熟知的“transclusion”)。Vue.js 实现了一个内容分发 API,参照了当前 Web Components 规范草案,使用特殊的 元素作为原始内容的插槽。
<div>
<h2>我是子组件的标题</h2>
<slot>
{{slot作用域}}
</slot>
</div>