前言
当前前端部署都是本地执行npm run build命令生成压缩包,然后放到服务器上面,解压到nginx 目录, 但是有的时候遇到一个问题,比如项目中某个成员升级了node_modules包写了一个组件packageA,但是其他组成员可能没有升级,导致上线后就会有一些问题,一个包还好,如果有好几个包都没有升级,那么可能导致一些意想不到的问题。后续行内会使用类似jenkins的自动化部署平台,需要通过脚本实现在线编译打包,显然当前模式是行不通的,所以需要把代码中应用到的包管理起来。
node包管理介绍
目前包的管理工具有npm(2010、1)、cnpm、yarn(2016)、pnpm(2017),建议包管理器使用优先级:pnpm > yarn > npm > cnpm;<br />在 npm 中安装依赖时,安装任务是串行的,会按包顺序逐个执行安装,这意味着它会等待一个包完全安装,然后再继续下一个。<br />为了加快包安装速度,yarn 采用了并行操作,在性能上有显著的提高。而且在缓存机制上,yarn 会将每个包缓存在磁盘上,在下一次安装这个包时,可以脱离网络实现从磁盘离线安装。yarn 更大的贡献是发明了 yarn.lock,npm是在一年后的 v5 才发布了 package-lock.json;所以 yarn 在出现时被定义为快速、安全、可靠的依赖管理。<br />**缺点:**yarn 依然和 npm 一样是扁平化的 node_modules 结构,没有解决**幽灵依赖**和**依赖分身**问题;<br />pnpm定义为快速的,节省磁盘空间的包管理工具,开创了一套新的依赖管理机制;<br />**优势:**pnpm 的安装速度在大多数场景都比 npm 和 yarn 快 2 倍,节省的磁盘空间更多;
- 幽灵依赖问题:只有直接依赖会平铺在 node_modules 下,子依赖不会被提升,不会产生幽灵依赖。
- 依赖分身问题:相同的依赖只会在全局 store 中安装一次。项目中的都是源文件的副本,几乎不占用任何空间,没有了依赖分身。
缺点:
- 由于 pnpm 创建的 node_modules 依赖软链接,因此在不支持软链接的环境中,无法使用 pnpm,比如 Electron 应用。
- 因为依赖源文件是安装在 store 中,调试依赖或 patch-package 给依赖打补丁也不太方便,可能会影响其他项目;
npm介绍
website:registry.npmjs.org
要使用npm进行开发,首先得拥有一个npm账号,网站上直接注册即可(用户名,邮箱,密码)
public package(默认公有包),private package(私有包,可以设置权限,需要付费)
创建package
// 新建包目录
md my-pkg
// 进入包目录
cd my-pkg
// npm 初始化,生成package.json
npm init -y
基础属性 | 描述 |
---|---|
name | 必需。package的名称 |
version | 必需。package的版本 |
description | 推荐。用来描述你的package的一些信息,方便别人在npm website上查找你的pacakge |
author | 作者的相关信息。格式Your Name email@example.com (example.com)。包含了你的名称、邮箱和博客之类的网址,不一定要全写 |
contributors | 其他贡献者 |
private | 你的package是私有的还是公有的 |
main | 包加载时入口文件 |
scripts | 一些npm脚本,形成npm指令,方便你进行某些工作,如构建 |
dependencies | 这是你的package发布后,罗列的所需要依赖的一些别人的package,缺乏了这些package,你的package也会不能如期正常工作 |
devDependencies | 这里是罗列你在开发过程中所依赖的一些package,为了方便你的开发所用的包,缺少他们也不会影响到你的包发布后的正常使用,就放在这里了。 |
repository | 源码地址 |
keywords | 包的搜索关键字 |
*重点配置:**
1、name 包名,为了避免包名已被使用,可以提前在npm中搜索相关的名称字段, 看是否已被使用;
2、main 包引入地址, 当一个包被引入到项目中,被使用时,将通这个地址查找对应的入口文件;
3、version 版本号, 每次提交包到npm时,需要保证当前包版本与已发布的包版本不同,但没有要求版本的具体格式。例如: 现在线上的包版本为 v1.0.0, 我本地包的版本设置为 v1.1.1, 一般向上递增发布。
编码
打包工具可选择webpack或rollup
Webpack 和 Rollup都是现代前端项目构建工具,两者在作用和适用场景上有所不同;
Webpack 适用于构建 Web 应用,可以处理 JavaScript、CSS、图片等各种资源,功能非常丰富,包括代码分割、懒加载、热更新等特性,可以实现非常复杂的项目构建需求。
Rollup 要小巧的多,打包生成的文件更小,更干净,适用于构建 JavaScript 库和模块,可以将多个模块打包成一个文件,同时原生支持 Tree-Shaking(摇树优化,去除无用代码),主要面向的是 JavaScript 模块的打包和发布。
我们可以根据具体需求场景选择使用Webpack或Rollup。
webpack配置实例:
1、plugins插件
/**
* @Author : Evan-Pei
* @Date : 2022/9/20
* @Version : 1.0
* @Last Modified time : 2022/9/20
**/
const Webpack = require('webpack')
const { CleanWebpackPlugin }= require('clean-webpack-plugin')
const childProcess=require('child_process')
const userInfo=require("./package.json")
function getProjectInfo() {
return `
Project name:${userInfo.name}
Branch:${childProcess.execSync('git symbolic-ref --short -q HEAD').toString().trim()}
Version:${childProcess.execSync('git show -s --format=%h').toString().trim()}
Author:${childProcess.execSync('git show -s --format=%cn').toString().trim()}
Recent edit by ${childProcess.execSync('git show -s --format=%cn').toString().trim()}/${childProcess.execSync('git show -s --format=%ce').toString().trim()} on ${new Date().toLocaleString()}`
}
module.exports=function(){
let plugins=[
new CleanWebpackPlugin(),
new Webpack.BannerPlugin({
banner:getProjectInfo(),
exclude:[/vendor/,/manifest/,/common/]
})
]
return plugins
}
2、webpack.config.js
const path = require("path")
const getPlugins = require('./webpack.plugins')
module.exports = function(){
return {
// 模式
mode: "production", // 也可以使用 production
// 入口
entry: "./src/index.js",
// 出口
output: {
// 打包文件夹
path: path.resolve(__dirname, "dist"),
// 打包文件
filename: "hand-utils-tools.js",
// 向外暴露的对象的名称
library: "EHands",
// 打包生成库可以通过esm/commonjs/reqirejs的语法引入
libraryTarget: "umd",
},
plugins:getPlugins()
}
};
3、打包build.js
/**
* @Author : Evan-Pei
* @Date : 2022/9/22
* @Version : 1.0
* @Last Modified time : 2022/9/22
**/
const Webpack = require('webpack')
const ora=require('ora')
const getWebpackConf = require('./webpack.config.js')
const fse = require('fs-extra')
//开始执行webpack
const spinner = ora(`Building for production...`);
spinner.start();
Webpack(Object.assign(getWebpackConf(),{mode:'production'}), function (err, stats) {
spinner.succeed()
if (err) throw err
setTimeout(function(){
try {
fse.copySync('./dist/hand-utils-tools.js', './npm-apk/persagy-hands-utils-tools/index.js')
fse.copySync('./readme.md', './npm-apk/persagy-hands-utils-tools/readme.md')
console.log('success!')
} catch (err) {
console.error(err)
}
},2000)
})
rollup配置实例:
1、安装 rollup 及插件
npm i -D rollup
npm i -D rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-babel rollup-plugin-eslint
//创建 rollup.config.js 配置文件
type nul>rollup.config.js
2、配置rollup
import resolve from 'rollup-plugin-node-resolve'; // 依赖引用插件
import commonjs from 'rollup-plugin-commonjs'; // commonjs模块转换插件
import babel from "rollup-plugin-babel"; // babel 插件
export default {
input: './src/main.js', // 打包的入口文件
output:{
name: 'my-pkg', // 输入的包名
file: './bin/index.js', // 打包输出地址, 这里的导出地址就是package内main的地址
format: 'umd' // 包类型
},
plugins: [ // 使用的插件
resolve(),
commonjs(),
babel({
exclude: 'node_modules/**',
runtimeHelpers: true,
}),
],
ignore: [
"node_modules/**" // 忽略目录
]
}
3、添加打包命令
// package.json
{
....
"script": {
// 指定rollup运行的配置文件,并监听文件修改。
"dev": "rollup -c rollup.config.js -w",
"build": "rollup -c rollup.config.js",
}
// -c 执行配置文件
// -w 监听文件修改,当文件修改后将重新打包
}
4、调试
4.1、npm link是一个用于开发时直接将本地包链接为依赖项的一个命令行工具。通常用于发布npm包之前本地测试使用;通过 npm link 将本地包注册到全局, 这里我们在根目录下新建 test测试目录,模拟包的引入及使用;
4.2、创建test目录md test
cd test type nul>index.js
4.3、全局注册包
// 在包根目录下 /pkg npm link
4.4、引入本地包
// test 目录下 npm link my-pkg
将vue组件封装成插件:
vue插件的格式要通过 install 来挂载组件,使用 Vue.component 来注册一个插件,在外部 use 一个插件, index.js 的内容如下。
import VueHalfdayDatepicker from './VueHalfdayDatepicker.vue'
const datePicker = {
install: function(Vue) {
Vue.component(VueHalfdayDatepicker.name, VueHalfdayDatepicker)
}
}
// 这里的判断很重要
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(datePicker)
}
export default datePicker
发布到NPM
1、 设置npm registry(npm源)
默认地,我们发布的公共包,是发到registry.npmjs.org 上;更多时候,当你需要下载的公司的私有资源时,就需要设置对应的公司资源所在地址,即npm config set registry xxx(xxx为你的注册地址,简单粗暴)
2、使用nrm(npm registry manager )是npm的镜像源管理工具
- nrm ls :查看所有配置好的源以及对应名称
- nrm add company npm.xxx.cn:添加源,company 是名称,可以自行命名,后面是源的 url 地址
- nrm del company :删除源,根据名称 company 可以删除对应的源
- nrm test [registry] :测试源的速度,不加对应的 registry 名称,测试所有源的速度,添加对应的名称,比如 company,就是测试 company 对应的源的速度
- nrm use company :切换源,即可使用 company 对应名称的源
3、npm login 登录npm
4、npm publish 发布包
5、查看版本信息
查看当前模块的详细信息
$ npm info
查看某个 pkg 的最新版本
$ npm view pkg version
查看某个pkg 在npm服务器上所有发布过的版本
$ npm view pkg versions
6、包权限管理
很多时候,一个项目包往往不只是你一个人在管理的,这时需要给其他一起维护的同学开通发布的权限,相关使用命令如下:
# 查看模块 owner, 其中 pkg 为模块名称
$ npm owner ls pkg
添加一个发布者, 其中 xxx 为要添加同学的 npm 账号
$ npm owner add xxx pkg
删除一个发布者
$ npm owner rm xxx pkg
npm进阶
npm版本一般包括主版本(major), 次版本(minor),补丁版本(patch)
比如:3.2.1
3表示主版本, 指重大改版,不向下兼容
2表示次版本,通常是指增加feature功能,向下兼容
1表示补丁版本, 通常是指修改bug等
版本号前符号含义
^3.2.1 指定的主版本号下,所有更新的版本,匹配 3.2.3, 3.3.0;不匹配2.0.3, 4.0.1
~3.2.1 知道的主版本号.次版本号下,所有更新的版本,匹配 3.2.3,3.2.9;不匹配3.3.0,3.4.5
>=2.1 版本号大于或等于2.1.0,匹配2.1.2,3.1;不匹配 2.0,1.0
<=2.3 版本号小于等于2.2.0,匹配1.0.0,2.1.2;不匹配 2.3,2.4.1
1.0.0 - 2.0.0 版本号在1.0.0(含)到2.0.0(含)之间,匹配1.0.0,1.9.9;不匹配2.1.1
* 或者 x 匹配所有主版本
1 或 1.x 匹配主版本号为1的所有版本
1.2 或 1.2.x 匹配主版本号为1次版本号为2的所有版本
总结
更加详细的配置与介绍请参考官方介绍:docs.npmjs.com/about-npm