# Ship-Node

# 一、安装及配置nodejs

>>下载:http://nodejs.cn/
>>根据系统下载对应版本后直接运行安装
>>便于管理建议安装到自定义路劲,此处为:【D:\Work\CodeLang\NodeJs】
>>安装完成后cmd运行:
	node -v 
	npm -v
	yarn -v
>>如果没有yarn: 
	npm install -g yarn >> yarn -v 
>>如果提示无法找到内部命令:
	where yarn >> 将路劲添加到path环境变量即可:【C:\Users\vic\AppData\Roaming\npm\node_modules\yarn\bin】
>>切换淘宝镜像 
	yarn config set registry https://registry.npm.taobao.org
>>配置全局路劲:安装目录下新建目录【node_cache】和【node_global】
	npm config set cache  "D:\Work\CodeLang\NodeJs\node_cache"
	npm config set prefix "D:\Work\CodeLang\NodeJs\node_global"
	yarn config set global-folder "D:\Work\CodeLang\NodeJs\node_global"	
	yarn config set cache-folder "D:\Work\CodeLang\NodeJs\node_cache"	
>>添加系统环境变量:
	path:【D:\Work\CodeLang\NodeJs\node_global】
	NODE_PATH:【D:\Work\CodeLang\NodeJs\node_global】
>>安装webpack:
	npm install webpack -gD
	npm install webpack-cli -gD
	yarn add webpack -gD
	yarn add webpack webpack-cli -gD
>>安装指定版本:
	npm install webpack@<version> -g        
>>例如:
	npm install  webpack@4.41.2  -g
>>查看版本:
	webpack -v
>>在全局下删除:
	npm uninstall webpack -g
>>局部删除:
	npm uninstall webpack
>>至此安装及配置完成。
>>【注意:】
	对于git管理的项目:git push到github需要忽略的文件请参考配置文件【.gitignore】
	npm与yarn参数说明:
		-g (global) 全局安装的时候请带上-g,不带则为局部安装
		-s (save)
		-d (dev)
		-D (=--save-dev) 将模块安装到项目目录下,并在package文件的dependencies节点写入依赖
		init的时候带上-y为默认设置
>>yarn和npm/pnpm/cnpm命令对比:
    初始化             yarn init                                           npm init 
    安装依赖          yarn install 或者 yarn                      npm install pnpm install
    新增依赖          yarn add element-ui                        npm install element-ui --save pnpm i element-ui
    删除依赖          yarn remove element-ui                  npm uninstall element-ui --save …
    更新依赖          yarn upgrade                                   npm update pnpm update
    全局安装或删除   yarn global remove vue-cli          npm uninstall vue-cli -g …
    同时下载多个       yarn add axios vue-axios            npm install --save axios vue-axios
    更多命令请了解:
    https://yarn.bootcss.com/
    如果build遇到错误:Node Sass does not yet support your current environment: Windows 64-bit如何解决:
    重新安装node-sass
    yarn remove node-sass
    yarn add node-sass

# 二、webpack

#1.全局安装webpack
#-y是默认配置
npm init -y
yarn init -y
npm install webpack -g
yarn add webpack -g
yarn add webpack webpack-cli -g
#安装指定版本:
npm install webpack@<version> -g        
#例如:
npm install  webpack@4.41.2  -g
#在全局下删除    
npm uninstall webpack -g
#局部删除
npm uninstall webpack
#查看安装是否成功
webpack -v
#到项目文件下安装webpack
npm install webpack
#3.安装全局的webpack-cli   
npm install -g webpack-cli
#4.配置mode 
#默认有production和development两种模式可以设置
#命令行设置  
webpack --mode development
#5.新建入口  在项目文件目录下新建src文件夹,新建index.js文件入口
#6.文件打包   命令行输入 
webpack --mode development 或 webpack --mode production
#webpack将会默认打包,将./src/index.js文件打包成./dist/main.js文件(自动生成dist文件夹和main.js文件)
#7.建立html文件,在项目目录下建立html文件,可以直接引用dist/main.js文件。
#注意,我们的 script 引用的文件是 dist/main.js,而不是 index.js。
#这正是前端开发领域的一个趋势:
#开发的源文件(例子中的 index.js)与最终部署的文件(例子中的 dist/main.js)是区分开的,
#之所以这样,是因为开发环境与用户的使用环境并不一致。
#比如我们可以在开发环境使用 ES2017 甚至 ES2018 的特性,而用户的浏览器不见得支持,
#这也是 webpack 等打包工具的一个意义,它们能够辅助我们构建出在目标用户浏览器上正常运行的代码。
#8.其他参数配置
#我们如果需要配置webpack指令的其他参数,只需要在webpack –mode production/development后加上其他参数即可,如:
webpack --mode development --watch --progress --display-modules --colors --display-reasons
#实时刷新
#9.监控文件
#watch选项最为直观,但在默认情况下,watch选项是关闭状态。
#启用watch选项    
webpack --mode development --watch
#10.刷新浏览器
#https://github.com/webpack/webpack-dev-server
#https://webpack.js.org/configuration/dev-server/#devserver
#webpack-dev-server,一个基于expressjs的开发服务器,提供实时刷新浏览器页面的功能。
#安装webpack-dev-server
#首先在项目下安装 webpack-dev-server: 
npm install -g webpack-dev-server
#然后在命令行下执行
webpack-dev-server --mode development --output-public-path dist
#webpack-dev-server是一个轻量级的服务器,修改文件源码后,自动刷新页面将修改同步到页面上安装webpack-dev-server:
#①全局安装:
npm install webpack-dev-server -g 
#②在项目中安装并将依赖写在package.json文件中:
npm install webpack-dev-server --save-dev
#③使用命令
webpack-dev-server --mode development --output-public-path src
#完成自动刷新,指定publicPath,这部分很容易没有实时刷新。
#④默认的端口号是8080,如果需要8080端口被占用,就需要改端口,
webpack-dev-server --port 3000
#(将端口号改为3000),可以直接在webpack.config.js配置文件中配置devServer属性,开启热更新和port。
#⑤启动服务,输入localhost:端口号,就显示发布的所有根目录,如果项目根目录中没有index.html文件,
#就会在浏览器中列出项目根目录中的所有的文件夹。
#⑥当使用
webpack-dev-server --mode development --output-public-path src
#命令时,在每次修改文件,是将文件打包保存在内存中并没有写在磁盘里,
#这种打包得到的文件和项目根目录中的index.html位于同一级。
#使用webpack命令将打包后的文件保存在磁盘中例如在index.html文件中引入通过
#webpack-dev-server --mode development  --output-public-path src
#打包的build.js
<script src="build.js"></script>  
#在index.html文件中引入通过webpack命令打包的build.js 
<script src="./build/build.js"></script>
#--inline 内联模式,在开发服务器的两种不同模式之间切换。
#默认情况下, 应用程序将被启用内嵌模式。这意味着将在包中插入一个脚本来处理实时重装, 并且生成消息将出现在浏览器控制台中。
#--hot 启用热模块更换功能
#index.html入口文件是在根目录下,没有进行配置content-base,
#因为配置了之后会只打包配置的目录文件,默认是根文件。
#配置了output的publicPath
#(很重要,删掉之后就不能自动刷新了,应该是webpack-dev-server将每次打包的文件根据output设置生成在publicPath目录下,
#而文件本身依旧是手动打包的,无法查看到自动刷新打包的文件),
#只配置了端口,没有配置hot:true和inline:true(最开始配置了,但是有报错,所以删掉莫名OK了)
#⑦webpack自带的watch命令与webpack-dev-server的区别
#--watch是文件修改后自动打包,webpack-dev-server是修改后发布到服务器上
#⑧webpack-dev-server --mode development --content-base src --inline --hot//显示只针对src路径下的文件刷新,文件修改之后浏览器自动刷新,如果要打开的文件和打包的文件不在一个文件夹内,最好不要设定文件夹
 #11.打包css文件
#在项目目录下安装处理css文件的loader
#命令行输入:
npm install css-loader style-loader --save-dev
#css-loader //处理css文件
#style-loader //将css-loader处理后的文件作为样式标签<style>插入到html文件中
#在处理css文件的时候要指定loader,如在index.js文件里输入
require('style-loader!css-loader!./style.css')
#或者直接在命令行输入webpack --mode development --module-bind "css=style-loader!css-loader"
#12--progress(查看进度)
#13--display-modules(显示隐藏的模块)
#14 --display-reasons(显示打包原因)
 #15.配置,webpack需要传入配置对象,因此进行新建配置文件webpack.config.js,
 #或者使用node.js内置的path模块进行配置,并在它前面加上 __dirname这个全局变量。
 #可以防止不同操作系统之间的文件路径问题,并且可以使相对路径按照预期工作。
#①先写moudule.exports={};进行配置;
#②入口文件配置,entry="入口文件路径,如./src/js/main.js";
#③输出文件配置,output={path:__dirname+"输出文件路径,如/dist/js/bundle.js"};//要创建dist文件夹
#__dirname为运行时的当前路径;
#另一种方式,先定义const path = require("path");//引入nodejs的path模块
#然后在输出文件路径path:path.resolve(__dirname,"./dist/js/bundle.js");
#path.resolve()方法解析了当前路径,将相对路径改为绝对路径。
#④重新指定配置文件名
#webpack --config 文件名
#如webpack --config webpack.dev.config.js
#16.定义执行脚本,可以在package.json中设置
#在script中设置,如设置"webpack":
"webpack --mode development --config webpack.config.js --progress --display-modules --colors --display-reason",
#//--colors(彩色显示)
#直接执行上面的脚本npm run webpack
#17.entry配置(chunk),
#①字符串表示,单输入,所有依赖都要在入口文件中指定,如entry:"./src/js/main.js",
#②数组表示,多输入,两个需要打包到一起的文件可以在配置文件的entry中用数组表示,
#如entry:["./app/entry1", "./app/entry2"],//这两个文件将会打包到一起
#③对象表示(哈希),多页面入口,entry:{page1:"./page1",page2:["./src/a.js","./src/b.js"]},
#这三种方式都会把文件打包到输出文件中。
#18.output配置,
#①单个入口起点,就设置一个出口,如output:{filename:'bundle.js',path:'/dist/js'}
#②多个入口起点,可以设置name或者hash,如output:{filename:'[name].js',path:__dirname+'/dist/js'}
#或output:{filename:'[name]-[hash].js',path:__dirname+'/dist/js'}
#或output:{filename:'[name]-[chunkhash].js',path:__dirname+'/dist/js'}
#hash值可以认为是版本号或者MD5值保证每个文件的唯一性,每一次修改之后生成文件的hash值不一样,文件名不一样。
#③publicPath可以理解为占位符。当需要上线的时候可以将服务器地址设置到这个参数中,
#output:{path:'xxx',filename:'xxx',publicPath:'http://cdn.com/'}
#插件(plugin)
#插件是 webpack 的支柱功能。webpack 自身也是构建在 webpack 配置中用到的相同的插件系统之上。插件目的在于解决 loader 无法实现的其他事。
#19.插件html-webpack-plugin
#要引用之前先安装,在项目文件目录下安装 npm install html-webpack-plugin --save-dev
#安装好之后,在webpack.config.js配置文件中对插件的引用
#var htmlWebpackPlugin = require('html-webpack-plugin');//commonJS写法
#在module.exports中添加plugin部分进行插件初始化,
#插件列表,当多个bundle需要共享一些相同的插件时,CommonChunkPlugin可以将这些依赖项提取到一个共享包中,以免重复。
plugins:[
    new webpack.optimize.CommonsChunkPlugin({
        .....
    }),
    new htmlChunkPlugin({
        template:'index.html',//自定义模板
        filename:'index-[hash].html',//生成文件名
        inject:'head',//指定链接注入在<head>标签中还是<body>标签中,为false值时表示不自动注入文件中,需要手动设置
        title:'webpack demo',//传递参数,可以在index.html模板中引用
        minify:{//压缩html文件,具体参数设置可以查看官方文档
            
        }
    })
]
#index.html引用配置文件中的参数,JS语法模式,要使用JS语句可以使用<%%>将每行代码包裹起来。
#赋值可以使用<%=xxx %>,如<%=htmlWebpackPlugin.options.title%>就可以取到配置文件中定义的title的值。
#在配置文件中可以任意的配置参数向html文件进行传参。
#自定义引用的js文件可以直接写到html文件中
#如在html文件中相对应的位置写,<script src="<%=htmlWebpackPlugin.files.chunks.main.entry %>"></script>
#<script src="<%=htmlWebpackPlugin.chunks.a.entry%>"></script>
#chunk是文件入口
#以上是单文件引用的示例,多文件引用则需要调用多次的html-webpack-plugin插件,设置方式相同
#多页面使用同一个页面模板,可以定义htmlWebpackPlugin插件中的chunks参数,进行设置不同的页面引用不同的chunks,
#如设置chunks:['main','a']
#excludeChunks:['a'],//指出排除的chunk
#直接将公共初始化脚本嵌入到html页面中,inline方式,在html模板中加上脚本源码引用代码,
#如
<script type="text/javascript">
<%=compilation.assets[htmlWebpackPlugin.files.chunks.main.entry.substr(htmlWebpackPlugin.files.publicPath.length)].source()%>
</script>
//.substr()的作用是将删除publicPath部分的绝对路径获取文件的相对路径。
#按照文件顺序引用js文件可以手动设置for循环出htmlWebpackPlugin.files.chunks的entry值插入文件中。
#20.loader
#loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
# 
#本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
loader能够import导入任何类型的模块。
在webpack的配置中loader有两个目标:
①.test属性,用于表示出应该被对应的loader进行转换的某个或某些文件。
②.use属性,表示进行转换时,应该使用那个loader。
使用方式:
①配置,在webpack.config.js中指定
②内联,在每个import语句中显示指定loader
③CLI,在shell命令中指定
 
在webpack.config.js中配置loader
在module.exports中添加属性module
如安装babel插件(js编译器),使用此插件转换ES6代码,如何安装根据官网进行安装:
module:{
    rules:[
       { test:/\.js$/,
        exclude:/node_modules/,
        loader:"babel-loader"
        }
    ]
}
设置preset,指定preset(预配置)设置如何处理js文件
①在rules中设置query:{presets:['latest']}
②在根目录下创建一个.babelrc文件,其中内容为:
{
    "presets":["env"]
}
③在package.json中,增加babel属性:
"babel":{
"presets":["latset"]
}
 
21.优化
可以在配置文件中,设置打包范围,如exclude设置不处理哪些模块,include处理哪些文件下的内容。
具体可以看官方文档进行配置。

# 三、webpack4.x_demo

# VicUI >>>>> webpack4.X_demo

####################################################################################################################

yarn init -y
yarn add webpack -g
yarn add webpack -D
yarn add webpack webpack-cli -g
yarn add webpack webpack-cli -D
#查看版本
webpack -v
#如果提示不是内部命令,重新打开项目即可,或者用-D安装一遍再重新打开项目

####################################################################################################################

#新建新建两个文件夹,分别为src文件夹和dist文件夹,接下来再创建三个文件:
#index.html --放在dist文件夹中;
#hello.js --放在src文件夹中;
#index.js --放在src文件夹中;
#index.html中写下html代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Webpack Project</title>
</head>
<body>
<div id='root'></div>
<script src="bundle.js"></script>   <!--这是打包之后的js文件,不指定文件名的时候默认为main.js-->
</body>
</html>

#hello.js中导出一个模块:
module.exports = function() {
    let hello = document.createElement('div');
    hello.innerHTML = "Long time no see!";
    return hello;
  };

#index.js中引入这个模块hello.js
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

#相当于我们把hello.js模块合并到了index.js模块
#之后我们打包时就只需把index.js模块打包成bundle.js
#然后供index.html引用即可,这就是最简单的webpack打包原理。
#webpack 打包,然后打开index.html即可看到信息。
// webpack全局安装的情况下
webpack src/index.js --output dist/bundle.js  
// --output可简写为-o
#注:此处指定了打包生成的文件名为bundle.js如果不设置直接用webpack命令打包的话会默认为main.js

####################################################################################################################

#通过配置文件来使用webpack
#当前项目的根目录下新建一个配置文件webpack.config.js
#我们写下如下简单配置代码,目前只涉及入口配置(相当于我们的index.js,从它开始打包)和出口配置(相当于我们打包生成的bundle.js)
module.exports = {
    entry: __dirname + "/src/index.js", // 入口文件
    output: {
        path: __dirname + "/dist", //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    }
}
#注:__dirname是node.js中的一个全局变量,它指向当前执行脚本所在的目录
#平时我们看到的脚手架配置也比较喜欢采用node.js的path模块来处理绝对路径,所以我们也可以采用如下的写法,和上述的效果是一样的:
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    }
}
#注:path.join的功能是拼接路径片段
#有了这个配置文件,我们只需在终端中运行webpack命令就可进行打包,这条命令会自动引用webpack.config.js文件中的配置选项

####################################################################################################################

#更智能的打包方式
#现在只在终端中使用webpack命令来进行打包,要是以后在打包的同时还有更多的操作呢,那不是还得写上更多的命令?
#所以我们得想办法把这些命令都集成起来,这时候之前的package.json文件就派上用场了。
#现在的package.json文件大概就是如下这样
{
  "name": "webpack4.x_demo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  }
}
#修改如下:
{
  "name": "webpack4.x_demo",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
      "start": "webpack", //改成这样,注意使用时把注释删掉
    },
  "license": "MIT",
  "dependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  }
}
#注:package.json中的script会按你设置的命令名称来执行对应的命令。
#这样我们就可以在终端中直接执行yarn start命令来进行打包
#start命令比较特殊,可以直接yarn加上start就可以执行
#如果我们想起其他的名称,如build时,就需要使用yarn run加上build,即yarn run build命令。
#现在我们执行yarn start命令:打包成功。

####################################################################################################################

#构建本地服务器
#现在我们是通过打开本地文件来查看页面的,看起来总感觉比较low
#看别人用vue,react框架时都是运行在本地服务器上的,那我们能不能也那样呢?那必须的!
#webpack-dev-server配置本地服务器
#Webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js构建,它是一个单独的组件
#在webpack中进行配置之前需要单独安装它作为项目依赖
yarn add webpack-dev-server -D
#devServer作为webpack配置选项中的一项,以下是它的一些配置选项:
contentBase :设置服务器所读取文件的目录,当前我们设置为"./dist"
port :设置端口号,如果省略,默认为8080
inline :设置为true,当源文件改变时会自动刷新页面
historyApiFallback :设置为true,所有的跳转将指向index.html
#现在我们把这些配置加到webpack.config.js文件上,如下:
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",   // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    }
}
#我们继续在package.json文件中添加启动命令:
{
  "name": "webpack4.x_demo",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server --open"
  },
  "license": "MIT",
  "dependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  }
}
#我们把start命令名称改为了build,这样比较语义化,平时的脚手架也多数采用这个名称
#我们用dev(development的缩写,意指开发环境)来启动本地服务器
#webpack-dev-server就是启动服务器的命令,--open是用于启动完服务器后自动打开浏览器
#这时候我们自定义命令方式的便捷性就体现出来了,可以多个命令集成在一起运行
#即我们定义了一个dev命令名称就可以同时运行了webpack-dev-server和--open两个命令。
#现在在终端输入yarn run dev运行服务器:
#这样我们即可在
http://localhost:8088/
#中查看页面(退出服务器,可使用ctrl+c后,再按y确认,即可退出服务器运行)
  
####################################################################################################################

#Source Maps调试配置
#作为开发,代码调试当然少不了,那么问题来了,经过打包后的文件,你是不容易找到出错的地方的,Source Map就是用来解决这个问题的。
#通过如下配置,我们会在打包时生成对应于打包文件的.map文件,使得编译后的代码可读性更高,更易于调试
#webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map'  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
}
#配置好后,我们再次运行yarn run build进行打包
#这时我们会发现在dist文件夹中多出了一个bundle.js.map文件:
#如果我们的代码有bug,在浏览器的调试工具中会提示错误出现的位置,这就是devtool: 'source-map'配置项的作用。

####################################################################################################################

#Loaders
#loaders是webpack最强大的功能之一
#通过不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理
#例如把scss转为css,将ES66、ES7等语法转化为当前浏览器能识别的语法,将JSX转化为js等多项功能。
#Loaders需要单独安装并且需要在webpack.config.js中的modules配置项下进行配置,Loaders的配置包括以下几方面:
test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
loader:loader的名称(必须)
include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
options:为loaders提供额外的设置选项(可选)
#配置css-loader
#如果我们要加载一个css文件,需要安装配置style-loader和css-loader:
yarn add style-loader css-loader -D
#webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            }
        ]
    }
}
#我们在src文件夹下新建css文件夹,该文件夹内新建style.css文件:
/* style.css */
body {
    background: gray;
}
#在index.js中引用它:
import './css/style.css';  //导入css
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());
#这时我们运行yarn run dev,会发现页面背景变成了灰色。
#如果是要编译sass文件呢?

####################################################################################################################

 #配置sass
 #因为sass-loader依赖于node-sass,所以还要安装node-sass
yarn add sass-loader node-sass -D
#增加sass的rules:
#webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            }
        ]
    }
}

#在css文件夹中新建blue.scss文件:
/* blue.scss */
$blue: blue;
body{
    color: $blue;
} 
#在index.js中引入blue.scss:
#index.js 
import './css/style.css';   // 导入css
import './css/blue.scss';   // 导入scss

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());
#这时yarn run dev重新启动服务器,应该会出现如下结果:灰色背景,蓝色字体
#还有诸如图片loader、字体loader等就不一一列出来了,感兴趣的可前往webpack官网查看,都是一样的套路。

####################################################################################################################

#Babel
#Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:
#让你能使用最新的JavaScript代码(ES6,ES7...),而不用管新标准是否被当前使用的浏览器完全支持;
#让你能使用基于JavaScript进行了拓展的语言,比如React的JSX;

#Babel的安装与配置
#Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中
#webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包
#(用得最多的是解析ES6的babel-preset-env包和解析JSX的babel-preset-react包)。
#babel-preset-env的env表示是对当前环境的预处理,而不是像以前使用babel-preset-es2015只能针对某个环境
yarn add babel-core babel-loader babel-preset-env babel-preset-react -D
#webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            }
        ]
    }
}

#现在我们已经可以支持ES6及JSX的语法了,我们用react来试试,但使用react还得先安装两个模块react和react-dom。
yarn add react react-dom -D
#此处会有警告:babel-loader@8.0.6" has unmet peer dependency "@babel/core@^7.0.0".后续会讲
#把hello.js文件修改一下:
#hello.js
import React, {Component} from 'react'; // 这两个模块必须引入

let name = 'Alan';

export default class Hello extends Component{
    render() {
        return (
            <div>
                {name}
            </div>
        );
    }
}

#修改index.js文件:
#index.js 
import './css/style.css';  // 导入css
import './css/blue.scss';  // 导入scss

import React from 'react';
import {render} from 'react-dom';
import Hello from './hello'; // 可省略.js后缀名

render(<Hello />, document.getElementById('root'));

#此时运行yarn run dev后你可能会发现报错
#这是因为官方默认babel-loader | babel对应的版本需要一致: 即babel-loader需要搭配最新版本babel
#(其实安装的时候就有警告)
#两种解决方案:
#回退低版本
yarn add babel-loader@7 babel-core babel-preset-env -D
#更新到最高版本:
yarn add babel-loader @babel/core @babel/preset-env webpack -D
#这里采取的是第一个方案,回退后,再此运行yarn run dev,成功:

####################################################################################################################

#优化babel配置
#虽然babel完全可以在webpack.config.js中进行配置,但现在不是都提倡模块化嘛,也许之后babel膨胀了,增加了更多的配置项呢?
#那我们不如把它提取出来,把它放到根目录下的.babelrc文件下(webpack会自动调用.babelrc里的babel配置选项)。
#我们在项目根目录下新建.babelrc文件:
#webpack.config.js
const path = require('path');
module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模块
            }
        ]
    }
}

# .babelrc 使用时把注释删掉,该文件不能添加注释
{
    "presets": ["env", "react"]
}
#此时不出问题的话应该一切运行正常

####################################################################################################################

#插件(Plugins)
#插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
#Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西
#loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个
#插件并不直接操作单个文件,它直接对整个构建过程起作用。
#使用某个插件,需要通过npm进行安装,然后在webpack.config.js配置文件的plugins(是一个数组)
#配置项中添加该插件的实例,下面我们先来使用一个简单的版权声明插件。
#webpack.config.js 添加以下配置
const webpack = require('webpack');  // 这个插件不需要安装,是基于webpack的,需要引入webpack模块
plugins: [
    new webpack.BannerPlugin('版权所有,翻版必究')  // new一个插件的实例
]

#运行yarn run build打包后我们看到bundle.js文件显示如下:
/*! 版权所有,翻版必究 */!

##############################################################################################################

#自动生成html文件(HtmlWebpackPlugin)
#到目前为止我们都是使用一开始建好的index.html文件,而且也是手动引入bundle.js
#要是以后我们引入不止一个js文件,而且更改js文件名的话,也得手动更改index.html中的js文件名
#所以能不能自动生成index.html且自动引用打包后的js呢?HtmlWebpackPlugin插件就是用来解决这个问题的:
#首先安装该插件
yarn add html-webpack-plugin -D
#然后我们对项目结构进行一些更改:
#把dist整个文件夹删除;
#在src文件夹下新建一个index.template.html(名称自定义)文件模板
#(当然这个是可选的,因为就算不设置模板,HtmlWebpackPlugin插件也会生成默认html文件,这里我们设置模块会让我们的开发更加灵活),如下:
<!-- index.template.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Here is Template</title>
  </head>
  <body>
    <div id='root'>
    </div>
  </body>
</html>

#webpack.config.js中我们引入了HtmlWebpackPlugin插件,并配置了引用了我们设置的模板,如下:
#webpack.config.js
const path = require('path');  // 路径处理模块
const webpack = require('webpack');  // 这个插件不需要安装,是基于webpack的,需要引入webpack模块
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件

module.exports = {
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
    },
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,   
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模块
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        })
    ]
}
#然后我们使用yarn run build进行打包,你会发现,dist文件夹和html文件都会自动生成

#为什么会自动生成dist文件夹呢?因为我们在output出口配置项中定义了出口文件所在的位置为dist文件夹,且出口文件名为bundle.js
#所以HtmlWebpackPlugin会自动帮你在index.html中引用名为bundle.js文件
#如果你在webpack.config.js文件中更改了出口文件名,index.html中也会自动更改该文件名,这样以后修改起来是不是方便多了?

########################################################################################################

#清理/dist文件夹(CleanWebpackPlugin)
#你可能已经注意到,在我们删掉/dist文件夹之前,由于前面的代码示例遗留,导致我们的/dist文件夹比较杂乱
#webpack会生成文件,然后将这些文件放置在/dist文件夹中,但是webpack无法追踪到哪些文件是实际在项目中用到的。
#通常,在每次构建前清理/dist文件夹,是比较推荐的做法,因此只会生成用到的文件,这时候就用到CleanWebpackPlugin插件了。
yarn add clean-webpack-plugin -D
#webpack.config.js
老版本写法
...
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

module.exports = {
    ...
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        }),
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夹名称
    ]
}

新版本写法
...
const { CleanWebpackPlugin } = require("clean-webpack-plugin");// 引入CleanWebpackPlugin插件

module.exports = {
    ...
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        }),
        new CleanWebpackPlugin()
    ]
}

#插件的使用方法都是一样的,首先引入,然后new一个实例,实例可传入参数。
#现在我们运行yarn run build后就会发现,webpack会先将/dist文件夹删除,然后再生产新的/dist文件夹。
#出现警告:Entrypoint undefined = index.html
##webpack.config.js 中的 module.exports 新增 stats: { children: false },即可解决该警告

########################################################################################################

#热更新(HotModuleReplacementPlugin)

#HotModuleReplacementPlugin(HMR)是一个很实用的插件,可以在我们修改代码后自动刷新预览效果。
devServer配置项中添加hot: true参数。
#因为HotModuleReplacementPlugin是webpack模块自带的,所以引入webpack后,在plugins配置项中直接使用即可。
# webpack.config.js
...
const webpack = require('webpack');  // 这个插件不需要安装,是基于webpack的,需要引入webpack模块

module.exports = {
    ...
    devServer: {
        contentBase: "./dist", // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true, // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
        hot: true // 热更新
    },
    ...
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例 
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        }),
        new CleanWebpackPlugin(['dist']),  // 传入所要清理的文件夹名称
        new webpack.HotModuleReplacementPlugin() // 热更新插件 
    ]
}

#此时我们重新启动项目yarn run dev后,修改hello.js的内容,会发现浏览器预览效果会自动刷新
#(也许反应会比较慢,因为我们使用了source-map和其他配置的影响,后面代码分离的时候我们再处理)。

########################################################################################################

#项目优化及拓展
#代码分离
#在当前的开发环境都是提倡模块化,webpack自然不例外
#我们前面的webpack.config.js配置文件,其实也没配置多少东西就这么多了,要是以后增加了更多配置,岂不是看得眼花缭乱
#所以最好的方法就是把它拆分,方便管理:
#1. 我们在根目录下新建三个文件,分别为webpack.common.js、webpack.dev.js、webpack.prod.js
#分别代表公共配置文件、开发环境配置文件、生产环境(指项目上线时的环境)配置文件。
#2. 安装一个合并模块插件:
yarn add webpack-merge -D
#3. 将webpack.config.js的代码拆分到上述新建的三个文件中,然后把webpack.config.js文件删除,具体如下:
#webpack.common.js
const path = require('path');  // 路径处理模块
const webpack = require('webpack');  // 这个插件不需要安装,是基于webpack的,需要引入webpack模块
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入HtmlWebpackPlugin插件

module.exports = {
    stats: { children: false },
    entry: path.join(__dirname, "/src/index.js"), // 入口文件
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "bundle.js" //打包后输出文件的文件名
    },
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ['style-loader', 'css-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {
                test: /\.(scss|sass)$/,   // 正则匹配以.scss和.sass结尾的文件
                use: ['style-loader', 'css-loader', 'sass-loader']  // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
            },
            {                             // jsx配置
                test: /(\.jsx|\.js)$/,
                use: {                    // 注意use选择如果有多项配置,可写成这种对象形式
                    loader: "babel-loader"
                },
                exclude: /node_modules/   // 排除匹配node_modules模块
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),  // new一个插件的实例
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "/src/index.template.html")// new一个这个插件的实例,并传入相关的参数
        }),
        new webpack.HotModuleReplacementPlugin() // 热更新插件
    ]
}

#webpack.dev.js
const merge = require('webpack-merge');  // 引入webpack-merge功能模块
const common = require('./webpack.common.js'); // 引入webpack.common.js

module.exports = merge(common, {   // 将webpack.common.js合并到当前文件
    devServer: {
        contentBase: "./dist",   // 本地服务器所加载文件的目录
        port: "8088",  // 设置端口号为8088
        inline: true,  // 文件修改后实时刷新
        historyApiFallback: true, //不跳转
        hot: true     //热加载
    }
})

#webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const { CleanWebpackPlugin } = require("clean-webpack-plugin");// 引入CleanWebpackPlugin插件

module.exports = merge(common, { // 将webpack.common.js合并到当前文件
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    plugins: [
        new CleanWebpackPlugin()  // 所要清理的dist文件夹
    ]
})

# 设置package.json的scripts命令:
{
  "name": "webpack4.x_demo",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config webpack.prod.js",
    "dev": "webpack-dev-server --open --config webpack.dev.js"
  },
  "license": "MIT",
  "dependencies": {
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  },
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "7",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.2.0",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.13.0",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.0",
    "webpack-dev-server": "^3.9.0",
    "webpack-merge": "^4.2.2"
  }
}

#我们把build命令改为了webpack --config webpack.prod.js,意思是把打包配置指向webpack.prod.js配置文件
#之前我们只需要使用一个webpack命令为什么就可以运行了?因为webpack命令是默认指向webpack.config.js这个文件名称的
#现在我们把文件名称改了,所以就需要自定义指向新的文件,dev命令中的指令也同理。
#然后我们运行yarn run build和yarn run dev,效果应该和我们分离代码前是一样的。

#注:说到package.json文件,顺便就多提几句,因为也许有些朋友可能对我们安装模块时加的-D、-S或-g命令存在一些疑惑,因为不知道什么时候加什么尾缀。
#其实这个package.json文件是用于我们安装依赖的,可以把它当成一份依赖安装说明表
#就是如果我们把项目上传或者发给其他的开发同事,肯定不会把/node_modules文件夹也发送过去,因为这太大了,不现实也没必要。
#开发同事只需要有这份package.json文件,然后yarn install就可以把我们所需要的依赖都安装下来
#但前提是package.json文件上有记录,这就是安装模块时加上-D,-S命令的原因。
#-D的全称是--save-dev指开发环境时需要用到的依赖,会记录在package.json文件中的devDependencies选项中
#-S是--save是指生产环境也就是上线环境中需要用到的依赖,会记录在package.json文件中的dependencies选项中
#-g的全称是--global指安装全局命令,就是我们在本电脑的任何项目中都能使用到的命令,比如安装cnpm这个淘宝镜像命令就会用到-g命令。
#所以我们在安装模块时一定不要忘了加上对应的尾缀命令,让我们的模块有迹可循

########################################################################################################

#多入口多出口
#到目前为止我们都是一个入口文件和一个出口文件,要是我不止一个入口文件呢?下面我们来试试:
#在webpack.common.js中的entry入口有三种写法,分别为字符串、数组和对象
#平时我们用得比较多的是对象,所以我们把它改为对象的写法
#首先我们在src文件夹下新建two.js文件,名称任意。因为有多个入口,所以肯定得多个出口来进行一一对应了,所以entry和output配置如下:
#webpack.common.js
...
module.exports = {
    entry: {
        index: path.join(__dirname, "/src/index.js"), //多个入口文件
        two: path.join(__dirname, "/src/two.js")
    }, 
    output: {
        path: path.join( __dirname, "/dist"), //打包后的文件存放的地方
        filename: "[name].js" //打包后输出文件的文件名
    },
    ...
}
#two.js
function two() {
    let element = document.createElement('div');
    element.innerHTML = '我是第二个入口文件';
    return element;
}

document.getElementById('root').appendChild(two());

#然后我们运行yarn run build打包后发现/dist文件夹下会多出two.js文件
#同时index.html也会自动将two.js引入,然后我们运行yarn run dev显示成功

########################################################################################################

#增加css前缀、分离css、消除冗余css、分离图片
#增加css前缀
#平时我们写css时,一些属性需要手动加上前缀,比如-webkit-border-radius: 10px;
#在webpack中我们能不能让它自动加上呢?那是必须的,首先肯定得安装模块了:
yarn add postcss-loader autoprefixer -D
#安装好这两个模块后,在项目根目录下新建postcss.config.js文件:
#postcss.config.js
module.exports = {
    plugins: [
        require('autoprefixer')  // 引用autoprefixer模块
    ]
}
#在style.css中增加以下样式:
/* style.css */
body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 这个属性会产生前缀 */
}
#修改webpack.common.js文件中的css-loader配置:
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: [            
                    {loader: 'style-loader'}, // 这里采用的是对象配置loader的写法
                    {loader: 'css-loader'},
                    {loader: 'postcss-loader'} // 使用postcss-loader
                ]  
            },
            ...
        ]
    },
    ...
}

#然后我们运行yarn run dev后css样式中会自动添加前缀webkit

########################################################################################################

#分离css
#虽然webpack的理念是把css、js全都打包到一个文件里,但要是我们想把css分离出来该怎么做呢?
#加上@next是为了安装最新的,否则会出错
yarn add extract-text-webpack-plugin@next -D 
#安装完以上插件后在webpack.common.js文件中引入并使用该插件:
// webpack.common.js
...
const ExtractTextPlugin = require('extract-text-webpack-plugin') //引入分离插件

module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ExtractTextPlugin.extract({  // 这里我们需要调用分离插件内的extract方法
                    fallback: 'style-loader',  // 相当于回滚,经postcss-loader和css-loader处理过的css最终再经过style-loader处理
                    use: ['css-loader', 'postcss-loader']
                })
            },
            ...
        ]
    },
    plugins: [
        ...
        new ExtractTextPlugin('css/index.css') // 将css分离到/dist文件夹下的css文件夹中的index.css
    ]
}
#运行yarn run build后会发现/dist文件夹内多出了/css文件夹及index.css文件。

########################################################################################################

#消除冗余css
#有时候我们css写得多了,可能会不自觉的写重复了一些样式,这就造成了多余的代码,上线前又忘了检查
#对于这方面,我们应该尽量去优化它,webpack就有这个功能。
yarn add purifycss-webpack purify-css glob -D
#安装完上述三个模块后,因为正常来说是在生产环境中优化代码,所以我们应该是在webpack.prod.js文件中进行配置
#引入clean-webpack-plugin及glob插件并使用它们:
#webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 引入CleanWebpackPlugin插件

const path = require('path');
const PurifyCssWebpack = require('purifycss-webpack'); // 引入PurifyCssWebpack插件
const glob = require('glob');  // 引入glob模块,用于扫描全部html文件中所引用的css

module.exports = merge(common, {   // 将webpack.common.js合并到当前文件
    devtool: 'source-map',  // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
    plugins: [
        new CleanWebpackPlugin(['dist']),  // 所要清理的文件夹名称
        new PurifyCssWebpack({
            paths: glob.sync(path.join(__dirname, 'src/*.html')) // 同步扫描所有html文件中所引用的css
        })
    ]
})
#我们在style.css文件中增加一些多余的代码试试:
/* style.css */
body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 这个属性会产生前缀 */
}

.a{                 /* 冗余css */
    color: black;     
}

.b{                 /* 冗余css */
    width: 50px;
    height: 50px;
    background: yellow;
}
#然后我们运行yarn run build后发现打包后的index.css中是没有多余的.a和.b代码的:
/*! 版权所有,翻版必究 */
/* style.css */
body {
    background: #999;
}

#root div{
    width: 200px;
    margin-top: 50px;
    -webkit-transform: rotate(45deg);
            transform: rotate(45deg); /* 这个属性会产生前缀 */
}

/*# sourceMappingURL=index.css.map*/

########################################################################################################

#处理图片
#到目前为止我们还没讲到图片的问题,如果要使用图片,我们得安装两个loader:
#虽然我们只需使用url-loader,但url-loader是依赖于file-loader的,所以也要安装
yarn add url-loader file-loader -D
#然后在webpack.common.js中配置url-loader:
#webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,  // 正则匹配图片格式名
                use: [
                    {
                        loader: 'url-loader'  // 使用url-loader
                    }
                ]
            },
            ...
        ]
    },
    ...
}

#src 下新建images文件夹,添加图片coffee.png图片
#我们修改一下style.css,把背景改为图片背景:
/* style.css */
body {
    background: url(../images/coffee.png) top right repeat-y;  /* 设为图片背景 */
}

#root div{
    width: 200px;
    margin-top: 50px;
    transform: rotate(45deg); /* 这个属性会产生前缀 */
}

.a{
    color: black;
}

.b{
    width: 50px;
    height: 50px;
    background: yellow;
}
#运行yarn run dev后c查看显示
#但是背景图片变成了base64,因为webpack会自动优化图片,减少发送请求,但是如果我想把它变成路径的该怎么做?
#我们可以把webpack.common.js的loader配置更改一下,增加options选项:
#webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000  // 限制只有小于1kb的图片才转为base64,例子图片为1.47kb,所以不会被转化
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}
#然后我们运行yarn run build后,再运行yarn run dev,额,图片是没有转成base64了,但是图片怎么不显示了?
#问题就出在路径上,我们之前图片的路径是在../images文件夹下,但是打包出来后没有这个路径了
#图片直接和文件同级了,所以我们需要在webpack.common.js中给它设置一个文件夹:
#webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            ...
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000,  // 限制只有小于1kb的图片才转为base64,例子图片为1.47kb,所以不会被转化
                            outputPath: 'images'  // 设置打包后图片存放的文件夹名称
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}
#继续yarn run build打包再yarn run dev运行,我的天!图片还是不显示!
#调试工具上看图片路径有images文件夹了,但是我的../呢?
#这又涉及到配置路径的问题上了,我们还需要在css-loader中给背景图片设置一个公共路径publicPath: '../',如下:
#webpack.common.js
...
module.exports = {
    ...
    module: {
        rules: [
            {
                test: /\.css$/,   // 正则匹配以.css结尾的文件
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: ['css-loader', 'postcss-loader'],
                    publicPath: '../'  // 给背景图片设置一个公共路径
                })
            },
            {
                test: /\.(png|jpg|svg|gif)$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1000,  // 限制只有小于1kb的图片才转为base64,例子图片为1.47kb,所以不会被转化
                            outputPath: 'images'  // 设置打包后图片存放的文件夹名称
                        }
                    }
                ]
            },
            ...
        ]
    },
    ...
}
#现在再yarn run build打包再yarn run dev启动,OK!没毛病!

########################################################################################################

#压缩代码
#在webpack4.x版本中当你打包时会自动把js压缩了,而且yarn run dev运行服务器时,当你修改代码时,热更新很慢
#这是因为你修改后webpack又自动为你打包,这就导致了在开发环境中效率很慢,所以我们需要把开发环境和生产环境区分开来
#这时就体现出我们代码分离的便捷性了,webpack.dev.js代表开发环境的配置,webpack.prod.js代表生产环境的配置
#这时我们只要在package.json文件中配置对应环境的命令即可:
{
  ...
  "scripts": {
    "build": "webpack --config webpack.prod.js --mode production",
    "dev": "webpack-dev-server --open --config webpack.dev.js --mode development"
  },
  ...
  }
}
#--mode production表示打包时是生产环境,会自己将js进行压缩
#--mode development表示当前是开发环境,不需要进行压缩。
#这同时也解决了之前一直遗留的警告问题:

########################################################################################################

#总结
#到此基本把webapck常用的功能都走了一遍,更多功能请参考官方文档学习
#完整代码请前往github:https://github.com/sp1203/VicUI/tree/master/webpack4.x_demo

# 四、webpack4_vue2.x_demo

# VicUI >>>>> webpack4_vue2.x_demo
#简单搭建webpack4+vue2.X
#初始化(-y是代表默认设置)
yarn init -y
#添加依赖(-g是代表全局,全局已经安装过的,项目内安装不要带-g)【卸载用remove】
yarn add webpack -g
yarn add webpack webpack-cli -g
#查看是否安装成功
yarn -v
#在根目录创建index.html
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack4-vue-demo</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#创建src文件夹,并在src下创建index.js
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

alert("hello!webpack4!")

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#打包webpack,成功,会有warning,因为尚未设置模式
#多了一个dist文件夹,webpack4在打包时默认入口文件为src目录下的index.js,输出地址为dist文件夹,文件为main.js
#修改index.html代码,引入<script src="./dist/main.js"></script>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webpack4-vue-demo</title>
</head>
<body>
    <div id="root"></div>
    <script src="./dist/main.js"></script>
</body>
</html>

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#在浏览器中打开,可以发现页面能调用index.js里的内容了
####################################################################################################
#添加vue
yarn add vue
#添加vue-loader
yarn add vue-loader
#在src下创建App.vue
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

<template>
    <div>{{msg}}</div>
</template>
<script>
export default{
    data(){
        return{
            msg: 'hello! webpack4-vue-demo!'
        }
    }
}
</script>

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#修改index.js
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

import Vue from 'vue'
import App from './App.vue'

new Vue({
    el: "#root",
    render:h=>h(App)
})

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#打包会报错
#因为vue-loader需要以插件的形式引入,即使安装了,但是我们需要在webpack.config.js中配置
#在根目录下创建webpack.config.js
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
 
module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#其中也配置了mode,使其为开发模式development,这样在打包时就不会有warning了
#继续打包依旧报错,提示需要vue-template-compiler
#添加vue-template-compiler
yarn add vue-template-compiler
#打包,成功,打开index.html可以发现已经支持vue的语法了
webpack

# 五、vue_cli2_demo

# VicUI >>>>>>> vue_cli2_demo
#本demo为vue-cli2的学习过程
#准备环境,安装前台开发所需环境(node.js、npm或者cnpm或者yarn、设置淘宝镜像等等)

#####################################################################################

#安装vue-cli2
yarn init -y
#-g表全局-D=--save--dev(开发阶段的依赖)
yarn add vue-cli -gD
#查看全局安装的路劲
npm root -g
#查看安装结果(-V是大写)如果提示vue不是内部命令的话重启cmd即可
vue -V

#####################################################################################

#创建vue-cli2项目
vue init webpack projectName
#projectName不能出现大写字母,一般可用小写字母加数字区分
例如:vue init webpack test1
#? Project name test1
#? Project description A Vue.js project
#? Author ship
#? Vue build standalone
#vue-router()官方的路由:选择 Y
#ESLint(官方代码管理工具,统一代码风格):此处可选择 N
#其余默认,一路回车,详细选择如下
#install vue-router?是否安装vue的路由插件,需要就选y,否则就n(以下均遵循此方法)
#Use ESLint to lint your code?是否使用ESLint检测你的代码?
#(ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。)
#Pick an ESLint preset:选择分支风格
#选项有三个
#1.standard(https://github.com/feross/standard) js的标准风格
#2.Airbnb(https://github.com/airbnb/javascript) JavaScript最合理的方法,这个github地址说是JavaScript最合理的方法
#3.none (configure it yourself) 自己配置
#Setup unit tests? 是否安装单元测试(暂不详细介绍)
#Pick a test runner 选择一个单元测试运行器
#选项有三个(我选none)
#1.Jest(Jest是由Facebook发布的开源的、基于Jasmine的JavaScript单元测试框架)
#2.Karma and Mocha
#3.none
#Setup e2e tests with Nightwatch(Y/n)?是否安装E2E测试框架NightWatch(E2E,也就是End To End,就是所谓的“用户真实场景”。)
#Should we run 'npm install' for you after the project has been created?(译:项目创建后是否要为你运行“npm install”?这里选择包管理工具)
#选项有三个
#yes,use npm(使用npm)
#yes,use yarn(使用yarn)
#no,I will handle that myself(自己操作)
#一路回车到此等待安装完毕

#创建完成后cd到test1下,执行yarn run dev
#如果报错则在test1目录下执行yarn add vue-cli -gD后即可
#访问http://localhost:8080项目成功

#####################################################################################

#项目练习,自己的hello world:新增First.vue页面
#修改index.js,引入新增的页面:
import First from '@/components/First'
#修改对应routes,查看页面发现已经改变

#页面跳转
#新建A.vue,B.vue(同A)
<template>
  <div>
    <p>
       我是A。。。。。。。。
    </p>
    <p>
      <router-link to="/">返回</router-link>
    </p>
  </div>
</template>

#修改First.vue
<template>
  <div>
    <router-link to="/a">转向A页面</router-link>
    <router-link to="/b">转向B页面</router-link>
  </div>
</template>

#index.js配置路由
#引入相应页面:
import A from '@/components/A'
import B from '@/components/B'
{
  path: '/a',
  component: A
},
{
  path: '/b',
  component: B
}

#####################################################################################

#嵌套路由效果:children;[{},{}]
#一级组件跳转到二级组件,二级组件跳转到三级组件。。。。。。父子关系跳转
#创建A1.vue
<template>
  <div>
    <p>
       我是A1。。。。。。。。
    </p>
    <p>
      <router-link to="/a">返回上一级</router-link>
    </p>
    <p>
      <router-link to="/">返回首页</router-link>
    </p>
  </div>
</template>

#注册路由
#index.js
import A1 from '@/components/A1'
{
  path: '/a',
  component: A,
  children:[
    {
      path: '/A1',
      component: A1
    }
  ]
},

#A.vue添加:
<router-link to="/A1">转向A1页面</router-link>
#A转向A1,A作为父级页面,需要挂载子路由
<router-view></router-view>
#此时出现的效果是A转向A1后,父级跟子级页面的内容都会展现,因为A1配置为了A的children子页面
#将A1更改为与A平级,路由配置去掉children
{
  path: '/a',
  component: A
},
{
  path: '/b',
  component: B
},
{
  path: '/A1',
  component: A1
}
#查看页面跳转效果:A转向A1后不再显示A的内容
#实现所有页面显示首页的内容
#将A,B,A1页面的路由都配置在First的children里
export default new Router({
  routes: [
    {
      path: '/',
      name: 'First',
      component: First,
      children:[
        {
          path: '/a',
          component: A
        },
        {
          path: '/b',
          component: B
        },
        {
          path: '/A1',
          component: A1
        }
      ]
    }
  ]
})
#First的div内挂载路由
<router-view></router-view>
#查看跳转效果:不管跳转到哪个页面都能看到首页内容

#####################################################################################

#为什么不用#号
#目前为止我们跳转页面的时候会发现地址中带有#号:http://localhost:8080/#/A1
hash模式:地址中带有#号,#号后的不被后台获取;
history模式:具有对url历史记录进行修改的功能;
url需要传递参数时,#号不能满足需求;
history需要后台配合,处理404的问题;
#hash模式下,页面不存在不会抛404错误,只是点击没有反应,history模式下只要页面不存在就会抛404
#history模式也可以控制对后台的访问操作,所以推荐使用history模式
#index.js中添加路由模式
export default new Router({
  mode: 'history',
  routes: 
  ...
#刷新页面再点击调整操作查看效果:不再有#号了

#####################################################################################

#单独安装eslint
#如果诶有安装vue-router,单独安装
#安装到生成环境
yarn add eslint --save
#安装到开发环境
yarn add eslint --dev
yarn add eslint --save-dev
yarn add eslint -D
#由于eslint是代码规范所以应该安装在开发环境,而不是生产环境
#卸载
yarn remove eslint
#安装好后package.json配置中可以看到以下配置
"eslint": "^6.7.2",
#idea下打开的项目需要刷新项目文件才可以看见
#基于webpack模板创建项目的语法
vue init webpack prog1
#####################################################################################

# 六、vue_cli3_demo

# VicUI >>>> vue_cli3_demo
#本Demo为vue_cli3的学习

###########################################################################

#vue_cli3的安装
#卸载vue_cli2
yarn remove vue_cli -g
npm uninstall vue_cli -g
#安装的时候用的什么命令卸载的时候就用对应的命令
#安装vue_cli3
yarn add @vue/cli -gD
#vue_cli3支持图形化界面创建项目
#启动图像界面
vue ui
#进入地址即可访问
http://localhost:8000/
#命令行创建项目
vue create test3
#项目配置,不要选default
 Manually select features
#暂时安装以下两项即可(初期学习不建议安装eslint)
 (*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 (*) Router
 ( ) Vuex
 ( ) CSS Pre-processors
>( ) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing
#打Y选择history模式后回车即可
#记录文件选择
  In dedicated config files
> In package.json
回车再回车默认后续设置
#启动命令
yarn run serve
#或者
yarn serve
#创建First.vue
<template>
    <div>
        这是vue-cli3的第一个页面
    </div>
</template>

#修改router下的index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import First from '../components/First.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    component: First
  },
  {
    path: '/about',
    name: 'about',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

#查看页面效果

###########################################################################

#用cli3实现cli2的demo效果

#更改First.vue
<template>
    <div>
       <router-link to="/a">转向A页面</router-link>
       <router-link to="/b">转向B页面</router-link>
    </div>
</template>

#新增A.vue
#新增B.vue
#注册路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import First from '../components/First.vue'
import A from '../components/A.vue'
import B from '../components/B.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    component: First
  },
  {
    path: '/a',
    component: A
  },
  {
    path: '/b',
    component: B
  },
  {
    path: '/about',
    name: 'about',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

#此效果跳转不会显示主菜单,所以改成嵌套路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import First from '../components/First.vue'
import A from '../components/A.vue'
import B from '../components/B.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    component: First,
    children:[
      {
        path: '/a',
        component: A
      },
      {
        path: '/b',
        component: B
      }
    ]
  },
  {
    path: '/about',
    name: 'about',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

#挂载路由First.vue
<router-view></router-view>
#或者
<router-view/>

#查看效果

###########################################################################

#样式
#router-link属于a标签
#First.vue添加
<style>
  a{
    color:red;
    font-size: 20px;
  }
</style>
#或者添加对应class
<template>
    <div>
       <router-link class="ys1" to="/a">转向A页面</router-link>
       <router-link class="ys1" to="/b">转向B页面</router-link>
      <router-view/>
    </div>
</template>
<style>
  .ys1{
    color:red;
    font-size: 20px;
  }
</style>

#此种设置属于内部样式
#外部样式设置
#外部样式要写在public里面
#新建ys1.css
#引入外部样式
  @import "../../public/ys1.css";
#注意返回的层级数:First.vue的上一级为components,再上一级为src,src才是跟public同级,所以返回了两层
#如果需要更高级别的样式控制,也可以将样式加在index.html里
<link rel="stylesheet" type="text/css" href="ys1.css">

#刷新首页生效



# 七、vue_cli3_proj

# VicUI >>>> vue_cli3_proj
#vue_cli3练习项目

###########################################################################

#vue_cli3的安装
#卸载vue_cli2
yarn remove vue_cli -g
npm uninstall vue_cli -g
#安装的时候用的什么命令卸载的时候就用对应的命令
#安装vue_cli3
yarn add @vue/cli -gD
#vue_cli3支持图形化界面创建项目
#启动图像界面
vue ui
#进入地址即可访问
http://localhost:8000/
#命令行创建项目
vue create proj
#项目配置,不要选default
 Manually select features
#暂时安装以下两项即可(初期学习不建议安装eslint)
 (*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 (*) Router
 ( ) Vuex
 ( ) CSS Pre-processors
>( ) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing
#打Y选择history模式后回车即可
#记录文件选择
  In dedicated config files
> In package.json
回车再回车默认后续设置
#启动命令
yarn run serve
#或者
yarn serve