组件是实现应用中局部功能的代码(HTML,CSS,JS)和资源(图片,声音,视频)的集合,凡是采用组件方式开发的应用都可以称为组件化应用
模块是指将一个大的js文件按照模块化拆分规则进行拆分成的每个js文件, 凡是采用模块方式开发的应用都可以称为模块化应用(组件包括模块)
传统方式开发的一个网页通常包括三部分: 结构(HTML)、样式(CSS)、交互(JavaScript)
组件化方式开发的应用
创建组件调用Vue.extend({该配置项和new Vue的配置项几乎相同只是略有差别})
注册组件进行管理
components:{组件的名字:组件对象}
Vue.component('组件的名字(标签名)', 组件对象)
在容器中中以HTML标签的方式使用组件
组件的名字规范(不可以是HTML内置的标签名)
<script>
// 创建组件实例对象(结构HTML 交互JS 样式CSS)
const myComponent = Vue.extend({
template : `
-
{{index}},{{user.name}}
`,
// 每次调用data方法都会返回一个对象,这个对象中的数据是独立的
data(){
return {
users : [
{id:'001',name:'jack'},
{id:'002',name:'lucy'},
{id:'003',name:'james'}
]
}
}
})
// 创建组件时省略Vue.extend()
const myComponent = {
template : `
-
{{index}},{{user.name}}
`,
data(){
return {
users : [
{id:'001',name:'jack'},
{id:'002',name:'lucy'},
{id:'003',name:'james'}
]
}
}
}
// 在Vue实例中注册组件进行管理
const vm = new Vue({
el : '#app',
data : {
msg : '第一个组件'
},
// 局部注册组件的只能在父组件绑定的容器中使用
components : {
// userlist是组件的名字,myComponent组件实例对象
userlist : myComponent,
}
})
script>
<div id="app">
<h1>{{msg}}h1>
<userlist>userlist>
<userlist>userlist>
<userlist/>
div>
全局注册的组件可以在所有父组件绑定的容器中或模板语句中使用
<body>
<div id="app">
<h1>{{msg}}h1>
<userlogin>userlogin>
div>
<div id="app2">
<userlogin>userlogin>
div>
<script>
// 创建组件
const userLoginComponent = {
// template只能有一个根元素
template : `
用户登录
`,
data(){
return {
username : '',
password : ''
}
},
methods: {
login(){
// this是vc
alert(this.username + "," + this.password)
}
},
}
// 全局注册组件,自动调用Vue.extend()方法
Vue.component('userlogin', userLoginComponent)
// Vue实例
const vm2 = new Vue({
el : '#app2'
})
// Vue实例
const vm = new Vue({
el : '#app',
data : {
msg : '第一个组件'
},
})
script>
body>
组件的划分粒度很重要,粒度太粗会降低组件的复用性, 为了让复用性更强Vue 的组件也支持父子组件嵌套使用
<body>
<div id="root">div>
<script>
// 创建Y1组件
const y1 = {
template : `
Y1组件
`
}
// 创建X1组件
const x1 = {
template : `
X1组件
`
}
// 创建Y组件
const y = {
template : `
Y组件
`,
// 注册y1组件
components : {y1}
}
// 创建X组件
const x = {
template : `
X组件
`,
// 注册x1组件
components : {x1}
}
// 创建app组件
const app = {
template : `
App组件
`,
// 注册X组件,对象的属性名和属性值相同时可以省略属性值
components : {x,y}
}
// vm
const vm = new Vue({
el : '#root',
template : `
`,
// 注册app组件
components : {app}
})
script>
body>
new Vue({})配置项中的this是Vue实例vm(构造函数中的this指向新创建的对象), Vue.extend({})配置项中的this是VueComponent实例vc
Vue.extend()源码: 因为Sub是个局部变量, 所以每一次调用Vue.extend方法返回的都是一个全新的VueComponent构造函数(构造函数也是类型即对应一个对象)
<body>
<div id="app">
<h1>{{msg}}h1>
<user>user>
div>
<script>
// 创建组件
const user = Vue.extend({
template : `
user组件
`,
mounted(){
// this是VueComponent实例,user是一个全新的VueComponent构造函数(函数类型的对象)
console.log('vc', this === user)//false
}
})
// Vue实例
const vm = new Vue({
el : '#app',
data : {
msg : 'vm与vc'
},
components : {
user
},
mounted() {
// this是Vue实例
console.log('vm', this)
},
})
script>
body>
XX的原型对象是一个共享的对象且只有一个,获取原型对象有两种方式
prototype
:显示的原型属性(建议程序员使用的),可以通过函数.prototype
的方式获取原型对象 __proto__
:隐式的原型属性(不建议程序员使用的),可以通过实例.__proto__
的方式获取原型对象// 构造函数(函数本身也是一种类型,代表User类型有一个prototype属性)
function User(){}
// 通过函数获取User的原型对象
let x = User.prototype
// 通过User构造函数可以创建实例
let a = new User()
// 通过实例获取到Vip的原型对象
let y = a.__proto__
// 获取的是同一个原型对象
console.log(x === y) // true
// 以下不是给User构造函数扩展属性而给“User的原型对象”扩展属性
User.prototype.counter = 1000
// 通过a实例可以访问到这个扩展的counter属性
// 访问原理:首先去a实例上找counter属性,如果a实例上没有counter属性的话,会沿着__proto__这个原型对象去找counter属性
console.log(a.counter)
//console.log(a.__proto__.counter)
VueComponent.prototype.__proto__ = Vue.prototype
: 这行代码实现了Vue,vm,VueComponent,vc都共享了同一个Vue的原型对象
<body>
<div id="app">
<h1>{{msg}}h1>
<user>user>
div>
<script>
// 给“Vue的原型对象”扩展一个counter属性
Vue.prototype.counter = 1000
// 创建组件
const user = Vue.extend({
template : `
user组件
`,
mounted(){
// 通过this也就是vc可以访问到Vue原型对象上的属性
console.log('vc.counter', this.counter)
// msg是vm实例上的属性不是原型对象上的属性所以访问不了
//console.log('vc.msg', this.msg)
}
})
// user等价于VueComponent构造函数
console.log('user.prototype.__proto__ === Vue.prototype' , user.prototype.__proto__ === Vue.prototype)// true
// Vue实例
const vm = new Vue({
el : '#app',
data : {
msg : 'vm与vc'
},
components : {
user
},
mounted() {
// this是Vue实例
console.log('vm', this)
},
})
// vm实例可以访问
console.log('vm.counter', vm.counter)
console.log('vm.counter', vm.__proto__.counter)
script>
body>
单文件组件就是一个文件对应一个组件, 单文件组件的名字通常是xxx.vue,这个文件是Vue框架规定的只有它能够认识,浏览器无法直接打开运行
生成代码
export用于暴露数据,常见的暴露方式有分别暴露,统一暴漏,默认暴露
// m1.js文件中使用分别暴露属性和方法
export let school = '尚硅谷';
export function teach() {
console.log("我们可以教给你开发技能");
}
// m2.js文件中使用对象的方式统一暴露
let school = '尚硅谷';
function findJob(){
console.log("我们可以帮助你找工作!!");
}
export {school, findJob};
// m3.js文件中使用默认暴露(可以暴露任意类型,一般是个对象)
export default {
school: 'ATGUIGU',
change: function(){
console.log("我们可以改变你!!");
}
}
import用于导入js文件中暴露的数据
<body>
<script type="module">
// 通用的导入方式,m1,m2,m3分别存储了暴露的数据
import * as m1 from "./src/js/m1.js";
import * as m2 from "./src/js/m2.js";
import * as m3 from "./src/js/m3.js";
// 解构赋值形式通过对象的形式使用暴露的数据,属性方法重名时使用as关键字
// import {a, b} from ‘模块标识符'
import {school, teach} from "./src/js/m1.js";
import {school as guigu, findJob} from "./src/js/m2.js";
// default是个关键字不能直接使用,需要使用别名机制
import {default as m3} from "./src/js/m3.js";
// 导入的简便形式但是只能针对默认暴露,m3是别名存储了暴露的数据和方法
// import 任意名称 from ‘模块标识符’
import m3 from "./src/js/m3.js";
// 访问暴露的属性和方法需要加一层default结构
console.log(m3.default.school);
script>
body>
将所有模块的引入单独写到一个js文件中,在index,html中使用script标签直接引入这个js文件即可
//模块引入的入口文件
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import * as m3 from "./m3.js";
<body>
<script src="./src/js/app.js" type="module">script>
body>
创建vm的js代码就不再写成一个组件了, 将这段js代码写到一个main.js入口文件中即可
import App from './App.vue'
new Vue({
el : '#root',
// 使用组件
template : ` `,
// 注册App组件
components : {App}
})
不管是单文件组件还是非单文件组件,永远都包括三步:创建组件、注册组件、使用组件
App组件
X组件
X1组件
Y组件
Y1组件
剩下的HTML代码一般写到index.html文件中
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单文件组件title>
head>
<body>
<div id="root">div>
<script src="../js/vue.js">script>
<script src="./main.js">script>
body>
html>
组件化开发的流程: 浏览器不认识.vue文件并且也不认识 ES6 的模块化语法, 需要安装Vue脚手架
第一步: Node.js 的下载地址
第二步: 一路点击下一步,添加命令到PATH环境变量,剩下按照默认选项安装即可
第三步: 打开dos命令窗口,输入 npm -version
查看npm的版本号
第四步: 配置npm镜像npm config set registry https://registry.npm.taobao.org
, 执行npm config get registry
返回成功即表示设置成功
调用管理员界面,查看nodejs安装版本:输入node -v命令 ,以及查看npm版本:输入npm -v命令;
Vue 的脚手架(Command Line Interface)是Vue官方提供的标准化开发平台,为我们Vue的开发提供了一条龙服务
第一步:执行npm install -g @vue/cli
命令安装脚手架(全局方式:表示只需要做一次即可)
第二步:选择一个目录使用Vue2的方式创建项目vue_provue creat vue_pro
, 项目中自带脚手架环境(内置了 webpack loader),自带一个 HelloWorld 案例
第三步:在创建的项目根目录下vue_pro执行 npm run serve
命令编译Vue程序自动生成html css js代码放入内置服务器并自动启动服务(ctrl + c 停止服务)
更改全局模块安装路径和缓存cache的路径
(1)、打开nodejs安装目录,在里面创建node_global和node_cache两个文件夹
配置全局安装的模块路径和缓存路径;
在nodejs根目录创建node_global,node_cache文件夹;
以管理员身份打开命令窗口,配置安装路径以及缓存路径,分别执行以下命令即可;
npm config set prefix "D:\dev\nodejs\node_global"
npm config set cache "D:\dev\nodejs\node_cache"
右键此电脑——属性——高级系统设置——高级——环境变量——系统变量,
配置环境变量,然后添加,变量值名为:NODE_PATH,变量值为npm安装路径:D:\Program Files\nodejs\node_modules;
编辑用户变量的 path,将默认的 C 盘下的 C:\Users\18280\AppData\Roaming\npm, 在path中添加:D:\Program Files\nodejs以及D:\Program Files\nodejs\node_global路径;
配置cnpm,配置淘宝镜像,默认路径下载比较缓慢,配置淘宝镜像可以加速下载速度,打开命令窗口执行以下命令即可,如果不想配置cnpm,可直接跳过此步骤;
npm install -g cnpm --registry=https://registry.npm.taobao.org
1
3.4. 直接为npm设置淘宝镜像仓库即可,命令如下:
npm config set registry https://registry.npm.taobao.org
1
以管理员打开cmd,输入npm config list 命令,查看npm的配置信息
# 临时更改为淘宝镜像源
npm --registry https://registry.npm.taobao.org install node-red-contrib-composer@latest
2、 全局使用淘宝源
# 修改为淘宝镜像源
npm config set registry https://registry.npm.taobao.org
3、 全局使用官方源
# 修改为官方镜像源
npm config set registry http://www.npmjs.org
npm与cnpm其实没什么大的区别,npm默认的镜像时国外的,cnpm下载镜像是国内淘宝团队的,下载速度快点,其实直接将npm的下载仓库直接设置为淘宝镜像就可以了。
不同的是在项目中下载项目依赖时包时命令不同,用npm执行npm install -g ,用cnpm时,用cnpm install -g 命令就可以了
# 安装cnpm
npm install -g cnpm -registry=https://registry.npm.taobao.org
4、安装yarn ,使用npm命令,以管理员打开命令窗口输入:npm install yarn -g命令进行安装,安装完成后,
配置yarn的执行路径;在path中添加。D:\dev\nodejs\node_global\node_modules\yarn\bin
# 全局安装命令
npm install -g yarn
或
cnpm install -g yarn
打开命令窗口,输入yarn -v即可查看yarn 版本;
使用yarn config list命令即可看到node配置的全局路径、缓存路径、以及淘宝镜像路径,查看是否安装正确;
2、更改模块及缓存存储目录
同样先在yarn的安装目录下创建yarn_global和yarn_cache文件夹,并打开命令窗口执行以下命令。
# 全局安装目录
yarn config set global-folder D:\dev\nodejs\node_global\node_modules\yarn\yarn_global
# 缓存目录
yarn config set cache-folder D:\dev\nodejs\node_global\node_modules\yarn\yarn_cache
# 查看当前源
yarn config get registry
# 修改为淘宝镜像源
yarn config set registry https://registry.npm.taobao.org
# 修改为官方镜像源
yarn config set registry https://registry.yarnpkg.com
index.html文件只有一个容器,并且没有看到引入vue.js文件和main.js文件
DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %>title>
<title>欢迎使用本系统title>
head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.strong>
noscript>
<div id="app">div>
body>
html>
分析脚手架默认配置文件vue.config.js,具体配置项可以参考 Vue CLI 官网手册
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
// 保存时是否进行语法检查(如组件的名字应该由多单词组成), 默认值true表示检查,false表示不检查
lintOnSave : false,
// 配置入口文件的位置和名称
pages: {
index: {
entry: 'src/main.js',
}
},
编译template配置项的模板语句报错原因: Vue包含两部分, 一部分是Vue的核心,一部分是模板编译器(占整个vue.js文件体积的三分之一)
解决main.js文件中的无法编译template配置项的方式
// 这里默认引入的是dist/vue.runtime.esm.js(esm版本是ES6模块化版本)
import Vue from 'vue'
// 导入App组件(根组件)
import App from './App.vue'
// 关闭生产提示信息
Vue.config.productionTip = false
// 创建Vue实例
new Vue({
el : '#app',
// 完整写法
render(createElement){
// 创建了一个div元素
//return createElement('div', 'render函数')
return createElement(App)
}
// 箭头函数的简写形式,参数只有一个省略括号,函数体只有一条语句可以省略{}和retuen
render : h => h(App)
})
App组件
发送组件通过组件标签的属性=""的形式传递数据, 接收组件使用props配置项可以接收其他组件传过来的数据,让组件的数据变为动态数据
在App这个父组件当中先找到子组件Car,然后给Car这个子组件通过属性的形式传数据
this.$refs.组件标识.$refs.name
表示访问子组件的子组件属性
{{msg}}
子组件Car接收父组件传递过来的数据
品牌:{{brand}}
价格:{{cprice}}
颜色:{{color}}
Vip.vue和User.vue代码中都有相同的methods,为了复用可以使用mixins配置进行混入(混入时配置项没有限制, 之前所学的配置项都可以混入)
混入时的配置项冲突问题
混入的方式分为局部导入混入和全局混入两种
export const mix1 = {
methods: {
printInfo(){
console.log(this.name, ',' , this.age)
}
}
}
export const mix2 = {
methods: {
a(){
console.log('mixin.js a.....')
}
},
}
export const mix3 = {
mounted() {
console.log('mixin.js mounted...')
}
}
{{msg}}
姓名:{{name}}
年龄:{{age}}
{{msg}}
姓名:{{name}}
年龄:{{age}}
全局混入在所有的组件中(包括根组件root即vm)都自动导入混入的配置项
// 等同于引入vue.js文件
import Vue from 'vue'
// 导入App组件(根组件)
import App from './App.vue'
// 导入mixin配置项
import {mix1} from './mixin.js'
import {mix2} from './mixin.js'
import {mix3} from './mixin.js'
// 全局混入
Vue.mixin(mix1)
Vue.mixin(mix2)
Vue.mixin(mix3)
// 关闭生产提示信息
Vue.config.productionTip = false
// 创建Vue实例
new Vue({
el : '#app',
render : h => h(App)
})
插件给用来Vue做功能增强的(可插拔), 插件一般都放到一个plugins.js文件中(一般和main.js在同级目录)
插件对象的install方法有两个参数
定义一个插件功能是给Vue的原型对象扩展一个counter属性(通过vm和vc都可以访问)
export const p1 = {
install(Vue, a, b, c, d){
console.log('这个插件正在显示一个可爱的封面')
console.log(Vue)
console.log(a,b,c,d)
// 获取Vue的原型对象,给Vue的原型对象扩展一个counter属性,通过vm和vc都可以访问
Vue.prototype.counter = 1000
}
}
// 等同于引入vue.js文件
import Vue from 'vue'
// 导入App组件(根组件)
import App from './App.vue'
// 导入插件
import {p1} from './plugins.js'
// 插上插件(删除就是拔下插件),通常放在创建Vue实例之前
Vue.use(p1, 1,2,3,4)
// 关闭生产提示信息
Vue.config.productionTip = false
// 创建Vue实例
new Vue({
el : '#app',
render : h => h(App)
})
默认情况下vue所有组件中定义的样式最终会汇总到一块,如果样式名一致会导致冲突,此时以后来加载的样式为准(就近原则)
npm i less-loader
由于样式冲突那么User组件的样式会覆盖掉Vip组件的样式
{{msg}}
{{msg}}