v-slot插槽、模块化开发、webpack、vue格式文件
目的是为了让组件的扩展性提高
插槽就是复用性组件的预留空间。组件抽取共性保留不同
<body>
<div id="app">
<cpn><button>按钮button>cpn>
<cpn><span>spanspan>cpn>
<cpn><i>我歪了i>cpn>
div>
body>
<script src="../js/vue.js">script>
<template id="cpn">
<div>
<h2>123h2>
<h3>下面那个是插槽h3>
<slot>插槽默认值slot>
div>
template>
<script>
const app=new Vue({
el:'#app',
data:{
message:'您好'
},
components:{
cpn:{
template:"#cpn"
}
}
})
script>
插槽定义时起名:name="插槽名"
插槽使用时给替换元素写上 slot=“插槽名” 找到对应的插槽:
<body>
<div id="app">
<cpn><p>只会替换无名字的p><button slot="center">我来替换中间插槽button>cpn>
div>
body>
<script src="../js/vue.js">script>
<template id="cpn">
<div>
<slot><p>左边插槽p>slot>
<slot name="center">中间插槽slot>
<slot><p>右边插槽p>slot>
div>
template>
<script>
const app = new Vue({
el: '#app',
data: {
message: '您好'
},
components: {
cpn: {
template: "#cpn"
}
}
})
script>
vue中的作用域可以理解成你在标签中用v-指令进行操作时找的是root级标签app中的属性和方法,还是组件中的属性和方法。
作用域插槽总结来说就是: 父组件替换插槽的标签,但是内容由子组件提供。
下面代码为一个普通的数组展示的代码,组件展示其内部的数组。
<body>
<div id="app">
<cpn>cpn>
div>
body>
<template id="cpn">
<div>
<ul>
<li v-for="item in pLanguages">{
{item}}li>
ul>
div>
template>
<script>
const app = new Vue({
el: "#app",
data: {
message: "123"
},
components: {
cpn: {
template: "#cpn",
data() {
return {
pLanguages: ["html", 'css', 'JavaScript']
}
}
}
}
})
script>
现在我们希望父组件(root)在调用该子组件时对v-for的每一步添加一些其他操作,这就需要我们的父组件可以访问到子组件内的data数据,这就要用到我们的作用域插槽
<cpn>
<template slot-scope="slot">
<span v-for="item in slot.data">{
{item}}--span>
template>
cpn>
div>
body>
<template id="cpn">
<div>
<slot :data="pLanguages">
<ul>
<li v-for="item in pLanguages">{
{item}}li>
ul>
slot>
div>
template>
我们先来看看为什么要使用模块化
现在前端有俩人联合开发。
//小明的aaa.js
var name="小明"
var flag=true
function sum(num1,num2) {
return num1+num2;
}
//小明又写了个bbb.js
if (flag){
console.log("flag为true");
}
//小红的hhh.js
var flag=false
结果在页面上这样引用:
<script src="../js/aaa.js">script>
<script src="../js/hhh.js">script>
<script src="../js/bbb.js">script>
小明的flag就会被小红覆盖。
于是我们想到用闭包匿名函数来封装这些变量,这样这些变量的作用域就变成匿名函数作用域了
//小明的aaa.js
(function f() {
var name="小明"
var flag=true
function sum(num1,num2) {
return num1+num2;
}
})();
这又带来了一个问题,这闭包里的属性和方法咋访问啊。
于是我们终于想到了模块化,匿名函数通过返回一个对象来储存需要的数据,搞一个唯一标识的全局变量指向这个对象。
//小明的aaa.js
var moduleA=(function f() {
var returnObj={
}
var name="小明"
var flag=true
function sum(num1,num2) {
return num1+num2;
}
returnObj.name=name;
returnObj.flag=flag;
returnObj.sum=sum;
return returnObj;
})();
//小明又写了个bbb.js
if (moduleA.flag){
console.log("flag为true");
}
常见的模块化规范:CommonJS AMD CMD ES6的Modules
nodejs中有大量应用。
//导出 aaa.js
module.exports={
flag:true,
test(a,b){
return a+b;
}
}
//导入
var {
flag,test}=require('aaa.js')
var aaa=require('aaa.js')
aaa.flag
//小明写的aaa.js
var flag=true;
function test(a,b) {
return a+b;
}
//导出数据
export {
flag,test
}
//小红写的bbb.js
var flag=false;
//小明的ccc.js 导入自己的aaa.js
import {
flag,test} from "./aaa.js";
import getDefault from './aaa.js'
if (flag){
console.log(1);
}
console.log(test(1, 2));
注意:在页面中引用是有说法的
<script src="aaa.js" type="module">script>
<script src="bbb.js" type="module">script>
<script src="ccc.js" type="module">script>
<script>
console.log(flag);//flag is not defined
script>
通过type=“module” 可以把引入的script做成一个单独的作用域,此时页面上已经无法直接访问其中的数据。
所有人都可以通过import来访问你export中暴露的数据。
导出有很多种写法
//导出方式2
export var num1=200;
//导出函数/类
export function sum(a,b) {
return a+b;
}
export class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
run(){
console.log(`${
this.age}岁的${
this.name}在跑`);
}
}
//export default
//某些情况下一个模块的功能不希望给这个功能命名,就可以用export default,注意每个js只能这样导出一个数据
export default "default数据"
//可以导出函数
//export default function (argu) {
// console.log(argu);
//}
export default的导入写法: getDefault 为使用者自定义的名字,写啥都行。
import getDefault from './aaa.js'
导入方法2
import * as aaa from './aaa.js';
console.log(aaa.flag);
aaa.default()
一般都是这么干,避免与当前js有冲突。意思就是把aaa.js导出的东西全部封装到aaa对象中
webpack是js的模块化打包工具。那么另一个前端打包工具grunt/gulp与webpack相比有何异同呢。
grunt/gulp的核心是task
我们可以配置一些类的task ,定义器执行的事情(es6转化,ts转化,图片压缩等)
之后让grunt/gulp 去执行这些task
所以grunt/gulp也被称为前端自动化任务管理工具
但是grunt/gulp对模块化是没有支撑的,webpack主要就是强调模块化的开发管理,grunt/gulp的功能webpack也有。
webpack依赖于node环境,npm工具用来管理node的代码包(node packages manager)
这是我们项目目前的结构
我们的main.js 和mathUtils.js 如下
const {
add, multi} = require('./mathUtils')
console.log(add(20, 30));
console.log(multi(20, 30));
function add(num1,num2) {
return num1+num2;
}
function multi(num1,num2) {
return num1*num2;
}
module.exports={
add,multi
}
这里是使用commonJS的语法完成模块化js的,正常通过浏览器引入的话页面是无法解析的。
这时候webpack就登场了,在terminal使用指令
webpack ./src/main.js ./dist/bundle.js
通过webpack将main.js解析打包到dis文件夹下的bundle.js里,webpack在处理main.js时会看他是否有引用,有的话会连带着引用一起解析。下面是执行的结果。
之后我们只要在页面中引入这个bundle.js就可以实现所有的功能了。除了commonJs,AMD CMD es6module等模块化工具都可以使用webpack进行打包处理。
所以我们目前的开发应该是这样:
首先创建一个js文件:webpack.config,webpack执行时会看这个webpack.config.js里有没有导出入口(entry)和出口(output)这俩参数。有的话就按照这俩参数执行
但是这个output.path他需要一个绝对路径,为了获取当前路径,我们要用一点node的知识。
我们先使用 npm install 来安装node的基本依赖,引用path模块,调用path.resolve(__dirname,"./dist")
它可以把当前路径(__dirname)和后面哪个拼接起来作为一个新的路径。
const path=require('path');
module.exports={
entry:'./src/main.js',
output:{
//这里需要一个绝对路径,所以这里要动态获取路径
path:path.resolve(__dirname,"./dist"),
filename:'bundle.js'
}
};
npm install之后,在package.json中,有一个scripts对象,这个就是用来控制命令的,你可以自定义命令名称和内容方便进行开发。而且这里的指令会优先执行本地安装的包,而直接webpack指令执行的是全局安装的包。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
依赖可以分为,开发时依赖(像webpack就是专门用来打包的,运行时就没他事了)和运行时依赖,一般情况我们需要使用的包都是安装在本地的。在当前文件夹下安装webpack
npm install [email protected] --save-dev //开发时依赖就这样安装--save-dev
安装完package.json中多一个,这就是开发时依赖的意思,与之相对的是dependencies(运行时依赖)
"devDependencies": {
"webpack": "^3.6.0"
}
调用自定义语句时使用:(build为指令名)
npm run build
首先我们在main.js中引入css
require('./css/normal.css')
然后我们安装一下cssloader
npm install --save-dev css-loader
然后我们在webpack.config.js中添加如下配置(在官网找)
module: {
rules: [
{
test: /\.css$/,
use: ['css-loader']
}
]
}
这样我们就可以正确的解析css文件了,但是目前的css文件还无法添加到dom中去(页面上没效果)
我们还需要安装style-loader
npm install style-loader --save-dev
修改配置文件中的module
这个rules的原理是通过正则test匹配文件,然后使用下面的loader,使用loader时是从右向左读取的,所以要注意顺序(大部分官网都有)
module: {
rules: [
{
//通过正则匹配下面的文件,然后使用下面的loader,使用loader时是从右向左读取的,所以要注意顺序
test: /\.css$/,
//css-loader只负责加载
use: ['style-loader','css-loader']
}
]
}
然后 npm run build
先写个less
@fontSize:50px;
@fontColor:orange;
body{
font-size: @fontSize;
color: @fontColor;
}
还是main.js 先依赖
require('./css/font.less')
下载loader 后面哪个less是用来把less转css的工具
npm install --save-dev less-loader less
添加配置
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
}
然后 npm run build
body{
background: url("../img/mvvm.jpg");
}
webpack对图片会处理为模块依赖,所以又需要loader
使用url-loader
安装:npm install --save-dev url-loader
配置:
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
}
]
}
build
//加载图片小于这个limit时会编译成Base64字符串
//想加载大的要再引入一个file-loader
安装完file-loader发现还是不行,不过打包完dist多了一个图
其实页面上已经找了这个图,只不过少了个dist路径
output: {
//这里需要一个绝对路径,所以这里要动态获取路径
path: path.resolve(__dirname, "./dist"),
filename: 'bundle.js',
publicPath:'dist/'
}
我们再配置文件的output中添加一个publicPath属性,该属性会让所有的url引用前都添加一个’dist/'字符
目前的图片名是32位hash,如何控制图片名称和路径呢?
通过options的配置属性name来控制,注意写的位置,写在rules数组的图片loader相关对象中
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10,
//img路径下,[name]会取出原来图片的名字,[hash:8]为8位随机不重复hash值,[ext]为当前的扩展名
name:'img/[name].[hash:8].[ext]'
},
}
]
}
webpack默认是es6的,要转es5 要安装一个babel的loader
npm install babel-loader babel-core babel-preset-env
配置:
{
test: /\.js$/,
//排除下面这个文件夹
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
我们正常开发时会借助脚手架不会像上面那样一个一个配的
先安装 npm install vue --save vue是运行时依赖
main.js中导入vue
import Vue from 'vue'
const app=new Vue({
el:'#app',
data:{
message:"123"
}
})
index 写id为app的div。这样会出错,因为vue这里有两类版本:
runtime-only 代码中不可以有任何的template(挂载给app也会被认为是template),他不会编译
rentime-compiler: 可以有template 因为有compiler可以用于编译template
这时我们是在使用runtime-only版本
解决方法:在webpack的配置文件中添加属性
resolve:{
//别名,这样他会跑到nodemodule的vue里面去找这个js,默认找那个是runtime-only的vue.js
alias:{
'vue$':'vue/dist/vue.esm.js'
}
}
这样使用vue我们要在页面上改页面,改完了还要去js里改很麻烦。所以我们可以这样做:
new Vue({
el:'#app',
template:`
{
{message}}
`,
data:{
message:"123"
},
methods:{
btnClick(){
console.log(1);
}
}
});
template 属性会替换你挂载的元素(#app)
这里我们遇到了一个问题,html页面报警告:找不到#app元素,原因是我们的bundle.js文件要引在我们dom树后面,引在前面还没加载到页面就执行了vue的代码。
终于要介绍.vue了。我们觉得上面那个代码很冗长,于是把他的template data methods都抽取出来捏成了一个组件,而new Vue只需要挂载元素和设置components组件即可。然后我们又把抽出来的组件整合到了一下,就产生了.vue文件
<template>
<div>
<h2 class="title" >{
{message}}h2>
<button @click="btnClick">按钮button>
div>
template>
<script>
export default {
name: "App",
data() {
return {
message: "123"}
},
methods: {
btnClick() {
console.log(1);
}
}
}
script>
<style scoped>
.title{
background: green;
}
style>
当然.vue文件 webpack还是无法解析的
npm install --save-dev vue-template-compiler vue-loader
vue-loader 14以上配置完还得配置一个插件,我们这里先改package改成13版本。
配置文件rules
{
test: /\.vue$/,
use: ['vue-loader']
}
目前的main.js
new Vue({
el:'#app',
template:" ",
components:{
App
}
})
版权所有插件
还是在webpack.config中
const webpack=require("webpack")
module.exports = {
plugins:[
new webpack.BannerPlugin("最终版权归咕咕咕所有")
]}
在bundle.js里可以看到这句话
html打包插件
我们的index.html肯定也要放到dist里,用到HtmlWebpackplugin
下载引入
npm install html-webpack-plugin --save-dev
//config
const HtmlWebpackPlugin = require("html-webpack-plugin");
plugins:[
new HtmlWebpackPlugin({
template: 'index.html' //他会将src下的index.html添加到dist中,并且自动引入bundle.js
})
]
js压缩插件(丑化) uglfiyjs-webpack-plugin
丑化就是把空格注释删了,名称全用简写
npm install [email protected] --save-dev
配置devServer
module.exports = {
devServer:{
//用于服务 dist文件夹
contentBase:'./dist',
inline:true, //页面实时刷新
port:8080 //端口 默认8080
}
}
配置package.json的scripts
"dev": "webpack-dev-server --open" //--open自动打开
不写的话,执行webpack-dev-server会跑到全局里面找webpack-dev-server,但是全局并没有安装,所以必须添加这个指令。这样我们开发时在src中修改内容,不需要webpack打包就可以通过devServer来访问,等我们觉得开发好了,再通过webpack打包发布。
根据开发依赖和发布时依赖,对配置进行分离.分别创建开发时依赖和生产依赖,再创建一个俩都用的依赖。开发时用base+dev 生产用base+prod。
npm install webpack-merge --save -dev
暴露时,调用webpackMerge的方法,他会把两个config整合
dev.config.js
//开发时配置
const webpackMerge=require('webpack-merge')
const baseConfig=require("./base.config.js")
//暴露时,调用webpackMerge的方法,他会把两个config整合
module.exports=webpackMerge(baseConfig,{
devServer:{
//开发服务器插件
//用于服务 dist文件夹
contentBase:'./dist',
inline:true, //页面实时刷新
port:8080 //端口 默认8080
}
})
prod.config.js
//生产
const uglifyjsWebpackPlugin=require("uglifyjs-webpack-plugin");
const webpackMerge=require('webpack-merge')
const baseConfig=require('./base.config.js')
module.exports = webpackMerge(baseConfig,{
plugins: [//js丑化插件
new uglifyjsWebpackPlugin()
]
});
剩下的放到base.config中
然后调整package.json中的脚本部分
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config ./config/prod.config.js",
"dev": "webpack-dev-server --open --config ./config/dev.config.js"
},
通过–config告诉他执行指令时去哪找配置文件
开发时用 npm run dev指令,发布时用 npm run build指令