项目github地址javascript
上一篇主要讲述了npm package的发布、更新、删除、开发过程当中的调试,以及拥有一个私有库的几种方式,这篇来说讲怎么把咱们写的代码编译打包(即各类语法转换成ES5)出来后,各个环境(浏览器、node)均可以使用,且不局限引用方式,便可以用ES6的import,node的require,以及script标签。咱们先从babel入手。css
babel is a JavaScript compilerhtml
Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments. Here are the main things Babel can do for you:前端
——摘抄 babeljava
The entire process to set this up involves:node
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill复制代码
2. Creating a config file named babel.config.json
in the root of your project with this content:react
{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1",
},
"useBuiltIns": "usage",
}
]
]
}复制代码
3. And running this command to compile all your code from the src
directory to lib
:jquery
./node_modules/.bin/babel src --out-dir lib复制代码
You can use the npm package runner that comes with npm@5.2.0 to shorten that command by replacing
./node_modules/.bin/babel
withnpx babel
webpack
——摘抄 babel 指南-Usage Guidegit
【提问:我想要在组件库中使用ES6/7/8/9等等最新的javascript语法,但是浏览器不兼容怎么办?】
@babel/preset-env
is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s). This both makes your life easier and JavaScript bundles smaller!
npm install --save-dev @babel/preset-env
yarn add @babel/preset-env --dev复制代码
【提问:个人组件库是用react写的,react又要怎么转换呢?】
This preset always includes the following plugins:
And with the development
option:
——摘抄 babel presets-react
npm install --save-dev @babel/preset-react
yarn add @babel/preset-react --dev复制代码
【小白提问:我打算用TypeScript来写个人组件库,避免我编程的时候犯的一些低级错误,对组件使用者也相对更友好一些,那ts又须要用什么转换呢?】
This preset includes the following plugins:
You will need to specify
--extensions ".ts"
for@babel/cli
&@babel/node
cli's to handle.ts
files.
npm install --save-dev @babel/preset-typescript复制代码
// presets逆序执行(从后往前)ts -> react -> ES6/7
// preset的参数怎么写,有哪些,请自行查阅官方文档,这里不展开
{
"presets": ["@babel/preset-env","@babel/preset-react","@babel/preset-typescript"]
}复制代码
Now luckily for us, we're using the env
preset which has a "useBuiltIns"
option that when set to "usage"
will practically apply the last optimization mentioned above where you only include the polyfills you need. With this new option the configuration changes like this:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage", // https://www.babeljs.cn/docs/usage#polyfill
}
],
"@babel/preset-react",
"@babel/preset-typescript"
]
}复制代码
【笔者理解】简单的来讲,useBuiltIns
设置为usage
,babel会自动import对应的modules,简单方便。参考
// In a.js
var a = new Promise();
// Out (if environment doesn't support it) import "core-js/modules/es.promise"; var a = new Promise(); // Out (if environment supports it) var a = new Promise();复制代码
编译装饰器
Simple class decorator
@annotation
class MyClass { }
function annotation(target) {
target.annotated = true;
}复制代码
若是legacy
字段设为true
的话,就要配合@babel/plugin-proposal-class-properties使用,且loose
要设置为true
,参考
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}复制代码
A plugin that enables the re-use of Babel's injected helper code to save on codesize.
Instance methods such as
"foobar".includes("foo")
will only work withcore-js@3
. If you need to polyfill them, you can directly import"core-js"
or use@babel/preset-env
'suseBuiltIns
option.
The plugin transforms the following:
var sym = Symbol();
var promise = Promise.resolve();
var check = arr.includes("yeah!");
console.log(arr[Symbol.iterator]());复制代码
into the following:
import _getIterator from "@babel/runtime-corejs3/core-js/get-iterator";
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
import _Symbol from "@babel/runtime-corejs3/core-js-stable/symbol";
var sym = _Symbol();
var promise = _Promise.resolve();
var check = _includesInstanceProperty(arr).call(arr, "yeah!");
console.log(_getIterator(arr));复制代码
——摘抄 babel 用法-transform-runtime
【笔者理解】能够自动引入对应Babel's injected helper code,同use @babel/preset-env
's useBuiltIns
option
基础配置ok了,其余的语法须要babel解析的话,能够再自行查找babel-plugins。
(说一下笔者的操做,先一顿狂写,而后编译一下,babel会报错,报啥错,就安装啥插件,简单粗暴。每次的错误都要用心记录下来哦,这样之后就能够提早安装好须要的各类babel plugins了)
ES6 Module的加载实现(比较了ES6和CommonJs的差别、循环加载等)
require
返回的值是被输出的值的拷贝,模块内部的变化也不会影响这个值)。基本用法
//a.js
module.exports = function () {
console.log("hello world")
}
//b.js
var a = require('./a');
a();//"hello world"
//或者
//a2.js
exports.num = 1;
exports.obj = {xx: 2};
//b2.js
var a2 = require('./a2');
console.log(a2);//{ num: 1, obj: { xx: 2 } }
复制代码
——摘抄 掘金 再次梳理AMD、CMD、CommonJS、ES6 Module的区别
异步加载,依赖前置,提早执行
//a.js
//define能够传入三个参数,分别是字符串-模块名、数组-依赖模块、函数-回调函数
define(function(){
return 1;
})
// b.js
//数组中声明须要加载的模块,能够是模块名、js文件路径
require(['a'], function(a){
console.log(a);// 1
});复制代码
异步加载,依赖就近,延迟执行
/** AMD写法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) {
// 等于在最前面声明并初始化了要用到的全部模块
a.doSomething();
if (false) {
// 即使没用到某个模块 b,但 b 仍是提早执行了
b.doSomething()
}
});
/** CMD写法 **/
define(function(require, exports, module) {
var a = require('./a'); //在须要时申明
a.doSomething();
if (false) {
var b = require('./b');
b.doSomething();
}
});
/** sea.js **/
// 定义模块 math.js
define(function(require, exports, module) {
var $ = require('jquery.js');
var add = function(a,b){
return a+b;
}
exports.add = add;
});
// 加载模块
seajs.use(['math.js'], function(math){
var sum = math.add(1+2);
});
复制代码
——摘抄 掘金 前端模块化:CommonJS,AMD,CMD,ES6
this
指向当前模块,ES6 Module this
指向undefined
;——摘抄 掘金 再次梳理AMD、CMD、CommonJS、ES6 Module的区别
// a.js
const function a() => {
console.log("this is in a");
}
export {
a,
}
// b.js
import { a } from "./a";
a(); // this is in a
复制代码
webpack 配置-output.libraryTarget
咱们但愿包能够在任何的环境下运行,支持常见的三种引用方式
因此输出的libraryTarget要配置为umd
libraryTarget: "umd"
- 将你的 library 暴露为全部的模块定义下均可运行的方式。它将在 CommonJS, AMD 环境下运行,或将模块导出到 global 下的变量。了解更多请查看 UMD 仓库。
webapck.config.json
var path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
library: "MyLibrary",
libraryTarget: "umd"
}
};复制代码
最终输出
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["MyLibrary"] = factory();
else
root["MyLibrary"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return _entry_return_; // 此模块返回值,是入口 chunk 返回的值
});复制代码
——摘自 webpack 配置-output.libraryTarget-模块定义系统-umd
webpack 配置-libraryTargets【这些选项将致使 bundle 带有更完整的模块头部,以确保与各类模块系统的兼容性。根据 output.libraryTarget
选项不一样,output.library
选项将具备不一样的含义。】
webpack 配置-externals【防止将某些 import
的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些
webpack 配置-targets【webpack能够编译成不一样环境下运行的代码,例如node、web(默认)】
安装如下依赖,配置一个最基础的webpack
npm install webpack webpack-cli -D复制代码
webpack.condig.js
const path = require('path');
module.exports = {
mode:'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
library: "MyLibrary",
libraryTarget: "umd",
publicPath: "./",
}
};复制代码
安装如下依赖
npm install @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
npm install babel-loader -D 复制代码
webpack编译ES6的配置以下:
// ./webapck.config.js
var path = require('path');
module.exports = {
mode: process.env.NODE_ENV,
entry: { index: './src/index.js' },
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
library: "MyLibrary",
libraryTarget: "umd"
},
externals:
!process.env.debug
? ["react", "react-dom"]
: {
React: "react",
ReactDOM: "react-dom"
},
module: {
rules: [
{
test: /\.(jsx|js)$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/env"],
plugins: ["@babel/plugin-transform-runtime"]
}
},
],
include: [path.resolve(__dirname, "src")],
exclude: /(node_modules|bower_components)/,
}
]
}
};复制代码
安装如下依赖
npm install typescript
npm install ts-loader -D复制代码
webpack编译TS的配置以下,具体分析见下一篇
module: {
rules: [
{
test: /\.(tsx|ts)$/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/env"],
plugins: ["@babel/plugin-transform-runtime"]
}
},
+ { loader: "ts-loader" }
],
include: [path.resolve(__dirname, "src")],
exclude: /(node_modules|bower_components)/,
}
]
},复制代码
// tsconfig.json
{
"compilerOptions": {
"declaration": true, // 生成相应的 .d.ts文件。
"declarationDir": "./types", // 生成声明文件的输出路径。
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
},
"allowSyntheticDefaultImports": true, // 容许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
"experimentalDecorators": true, // 启用实验性的ES装饰器。
"module": "ES6",
"target": "ES6",
"skipLibCheck": true, // 忽略全部的声明文件( *.d.ts)的类型检查。
"esModuleInterop": true, // 经过导入内容建立命名空间,实现CommonJS和ES模块之间的互操做性
"moduleResolution": "node", // 决定如何处理模块。或者是"Node"对于Node.js/io.js,或者是"Classic"(默认)。
"strict": true, // 启用全部严格类型检查选项。
"removeComments": false, // 删除全部注释,除了以 /!*开头的版权信息。
"jsx": "react", // 在 .tsx文件里支持JSX: "React"或 "Preserve"。
"sourceMap": true, // 生成相应的 .map文件。
"downlevelIteration": true // 当target为"ES5"或"ES3"时,为"for-of" "spread"和"destructuring"中的迭代器提供彻底支持
},
"exclude": ["node_modules", "build", "scripts", "**/*.css"] // 表示要排除的,不编译的文件
}
复制代码
安装如下依赖
npm install @types/react @types/react-dom
npm install @babel/preset-react -D复制代码
webpack编译react的配置以下:
options: {
- presets: ["@babel/preset-env"],
+ presets: ["@babel/preset-env", "@babel/preset-react"],
plugins: ["@babel/plugin-transform-runtime"]
}复制代码
要使用装饰器的语法的话,须要安装
npm install @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D复制代码
plugins: [
// https://babeljs.io/docs/en/babel-plugin-proposal-decorators // If you are including your plugins manually and using @babel/plugin-proposal-class-properties, make sure that @babel/plugin-proposal-decorators comes before @babel/plugin-proposal-class-properties. // When using the legacy: true mode, @babel/plugin-proposal-class-properties must be used in loose mode to support the @babel/plugin-proposal-decorators. [ "@babel/plugin-proposal-decorators", { // Use the legacy (stage 1) decorators syntax and behavior. legacy: true } ], ["@babel/plugin-proposal-class-properties", { loose: true }],
"@babel/plugin-transform-runtime"]复制代码
@babel/plugin-proposal-decorators
的legacy
设为true
的话须要配置@babel/plugin-proposal-class-properties
的loose
为true
, 详见文档
安装如下依赖
npm install antd
npm install babel-plugin-import -D复制代码
options: {
presets: ["@babel/env", "@babel/react"],
plugins: [
+ [
+ "import",
+ {
+ libraryName: "antd",
+ // libraryDirectory: "es", // 默认lib
+ style: true // `style: true` 会加载 less 文件
+ }
+],
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
"@babel/plugin-transform-runtime"
]
}复制代码
对less文件作如下处理
安装如下依赖
npm install less-loader css-loader style-loader -D复制代码
{
test: /\.less$/,
use: [
{
loader:"style-loader"
},
{
loader: "css-loader",
options: {
modules: {
// localIdentName: '[path][name]__[local]',
getLocalIdent: (context, _, localName) => {
if (context.resourcePath.includes("node_modules")) {
return localName;
}
return `demo__${localName}`;
},
},
},
},
{
loader: "less-loader",
options: {
lessOptions: {
// http://lesscss.org/usage/#command-line-usage-options
javascriptEnabled: true,
modifyVars: {
// "primary-color": "#1DA57A",
// "link-color": "#1DA57A",
// "border-radius-base": "2px",
// or
// https://github.com/ant-design/ant-design/blob/d2214589391c8fc8646c3e8ef2c6aa86fcdd02a3/.antd-tools.config.js#L94
hack: `true; @import "${require.resolve( "./src/assets/style/ui.config.less" )}";` // Override with less file
}
}
}
}
]
},复制代码
You can pass any Less specific options to the less-loader via loader options. See the Less documentation for all available options in dash-case.
——摘自 webpack less-loader
Global Variables
命令行写法 |
json配置写法 |
lessc --global-var="color1=red" |
{ globalVars: { color1: 'red' } } |
This option defines a variable that can be referenced by the file. Effectively the declaration is put at the top of your base Less file, meaning it can be used but it also can be overridden if this variable is defined in the file.
Modify Variables
命令行写法 |
json配置 |
lessc --modify-var="color1=red" |
{ modifyVars: { color1: 'red' } } |
As opposed to the global variable option, this puts the declaration at the end of your base file, meaning it will override anything defined in your Less file.
——摘抄 less官方文档