您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Node CLI如何構建微信小程序腳手架,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
具體如下:
目的
由于目前公司的 TOC
產品只要是微信小程序,而且隨著業務的擴展, 會有更多的需求,創建更多的小程序,為了讓團隊避免每次開發前花費大量時間做比如工程化的一些配置,以及保持每個項目的一致性, 所以決定做一個 Node CLI
來創建微信小程序腳手架
節省開發前期的大量時間,新項目可以很快開始業務開發
保證項目統一性,有利于團隊間的協作及工程化
提升團隊基建意識,從枯燥無味的業務開發中脫離出來,嘗試新的東西,即使很基礎很簡單
小程序選型
小程序的第三方框架有很多, 我接觸過的就有 taro
/ wepy
/ mpvue
,并且都有對應上線的項目。 在嘗試這些框架的過程中,對比原生小程序,有一些感想想分享出來:
第三方框架語法貼近vue/react, 開發者可以根據自己的特點選擇框架,學習成本相對較低
原生框架在CSS預處理,多端復用,狀態管理,自動構建這幾塊能力對比其他框架是欠缺的
第三方框架額外的工具包會使打包體積變大,每次構建花費時間,同時性能不如原生
第三方框架更新迭代很快,比如wepy@1.x/wepy@2, 導致舊項目的更新問題
小程序的特性更新迭代速度較快, 第三方框架會相對滯后
綜上所述,由于我們目前沒有多端復用的要求,并且有的小程序相對簡單,需要很短時間內開發完成, 最重要的是,其他的框架我都試過了,原生的還沒寫過,一個字,新鮮感!!:smile: ,所以最終當仁不讓地選擇了原生小程序,不得不說,原生大法就是妙啊! :clap::clap::clap::clap:
大體思路
這個功能是相對很基礎的,但是作為一個每天搬磚的業務仔來說,是個艱難的過程,也是個很好的學習機會。
在做之前,想找找個社區比較:ox::beer:的學習(抄)一下,短暫考慮后,果斷選擇 taro-cli
, 然后火速打開源碼,一頓操作(完全蒙圈),學習了一點之后,才開始上手
這個具體的實現思路我想到兩個
git clone
遠程倉庫作為模版下載到本地,再根據用戶輸入配置修改 .json
文件(比如 appId
)
template
就放在當前目錄中,直接`copy``, 之后的事等同
權衡之后,打算使用 lerna
作為管理工具, 其中模版也作為一個 npm 包
,用到的時候去 npm
下載,這么做我是為了方便管理,統一 push
/ publish
, 就是為了省事 :smile:。
最終思路:
暴露命令 —> 用戶交互輸入配置 -> 集合配置下載模版 -> 根據配置修改 .json
-> git init
+ 安裝依賴
開發 Node CLI
Lerna 項目搭建
知道 monorepo
的同學不需要我多說,其實就是把代碼放在一個倉庫里,結果包之間回想以來,發布繁瑣等問題, 這里我們就用到了 lerna
這個神器幫助我們做包的統一管理
// 創建項目 mkdir modoo-mini-program cd modoo-mini-program // 初始化 lerna init cd packages mkdir modoo-script mkdir modoo-template-mini mkdir modoo-mini // 安裝 modoo-script 依賴用于測試,無其他實際用處 lerna bootstrap // 安裝依賴 + npm link
安裝依賴
為了實現功能,我們需要安裝一些依賴包
commander 命令行工具,用于讀取命令參數,作對應操作
node-fs-extra 在 Node.js 的 fs 基礎上增加了一些新的方法,更好用,還可以拷貝模板。
chalk 可以用于控制終端輸出字符串的樣式, 調整顏色啥的
inquirer 用戶命令行交互,獲取用戶的交互配置數據,就像個提問板
ora 實現加載中的狀態是一個 Loading 加前面轉起來的小圈圈,成功了是一個 Success 加前面一個小鉤鉤。
log-symbols 日志彩色符號,用來顯示√ 或 × 等的圖標
獲取命令
首先第一步,要在用戶全局安裝之后,暴露出命令接口,需要在 packages.json
文件中加入如下內容
"bin": { "modoo-script": "./bin/modoo-script.js" },
之后在根目錄下創建 bin
文件夾 + bin/modoo-script.js
#!/usr/bin/env node const { program } = require("commander"); program .version(require("../package").version) // modoo-script --version .usage("<command> [options]") // init 命令,床架項目 .command("init [projectName]", "Init a project with default templete") .parse(process.argv); // 解析命令參數
然后需要注意的是, commander
支持 Git
風格的子命令處理,可以根據子命令自動引導到以特定格式命名的命令執行文件,文件名的格式是 [command]-[subcommand]
,例如:
modoo-script init => modoo-script-init modoo-script build => modoo-script-build
所以為了實現 init
命令,可以直接在 bin
文件目錄下添加 modoo-script-init.js
#!/usr/bin/env node const { program } = require("commander"); program .option("--name [name]", "項目名稱") .option("--description [description]", "項目介紹") .option("--framework", "腳手架框架") .parse(process.argv); const args = program.args; // 獲取命令參數 const { name, description, framework } = program; const projectName = args[0] || name; ......
用戶交互
獲取了命令參數后,根據參數轉到用戶交互界面,這里使用的是 inquirer
來處理命令行交互, 用法很簡單
const inquirer = require('inquirer') if (typeof conf.description !== 'string') { prompts.push({ type: 'input', name: 'description', message: '請輸入項目介紹!' }) } ...... inquirer.prompt(prompts).then(answers => { // 整合配置 this.conf = Object.assign(this.conf, answers); })
遠程模塊
這里較為折騰,一開始說了,我把模版作為 npm包
,具體查找,下載的過程如下
npm search
查找相應的模版 npm 包
在用戶選擇框架后對應所需的包,獲取它的詳細信息,主要是 tarball
用戶輸入完后,下載 tarball
到項目目錄,并修改 .json
文件配置
部分代碼如圖所示
// 一 npm search 查找相應的模版 npm 包 const { execSync } = require("child_process"); module.exports = () => { let list = []; try { const listJSON = execSync( "npm search --json --registry http://registry.npmjs.org/ @modoo/modoo-template" ); list = JSON.parse(listJSON); } catch (error) {} return Promise.resolve(list); };
// 二 返回 npm 數據 const pkg = require("package-json"); const chalk = require("chalk"); const logSymbols = require("log-symbols"); exports.getBoilerplateMeta = framework => { log( logSymbols.info, chalk.cyan(`您已選擇 ${framework} 遠程模版, 正在查詢該模版...`) ); return pkg(framework, { fullMetadata: true }).then(metadata => { const { dist: { tarball }, version, name, keywords } = metadata; log( logSymbols.success, chalk.green(`已為您找到 ${framework} 遠程模版, 請輸入配置信息`) ); return { tarball, version, keywords, name }; }); };
// 三 下載 npm 包 const got = require("got"); const tar = require("tar"); const ora = require("ora"); const spinner = ora( chalk.cyan(`正在下載 ${framework} 遠程模板倉庫...`) ).start(); const stream = await got.stream(tarball); fs.mkdirSync(proPath); const tarOpts = { strip: 1, C: proPath }; // 管道流傳輸下載文件到當前目錄 stream.pipe(tar.x(tarOpts)).on("close", () => { spinner.succeed(chalk.green("下載遠程模塊完成!")); ...... })
// 四 遍歷文件修改配置 const fs = require("fs-extra"); readFiles( proPath, { ignore: [ ".{pandora,git,idea,vscode,DS_Store}/**/*", "{scripts,dist,node_modules}/**/*", "**/*.{png,jpg,jpeg,gif,bmp,webp}" ], gitignore: true }, ({ path, content }) => { fs.createWriteStream(path).end(template(content, inject)); } ); // 遞歸讀文件 exports.readFiles = (dir, options, done) => { if (!fs.existsSync(dir)) { throw new Error(`The file ${dir} does not exist.`); } if (typeof options === "function") { done = options; options = {}; } options = Object.assign( {}, { cwd: dir, dot: true, absolute: true, onlyFiles: true }, options ); const files = globby.sync("**/**", options); files.forEach(file => { done({ path: file, content: fs.readFileSync(file, { encoding: "utf8" }) }); }); }; // 配置替換 exports.template = (content = "", inject) => { return content.replace(/@{([^}]+)}/gi, (m, key) => { return inject[key.trim()]; }); };
下載依賴
下載完畢并且修改完配置后, 默認執行 git init
+ 根據環境( yarn
/ npm
/ cnpm
)安裝依賴,這個就很簡單了
const { exec } = require("child_process"); const ora = require("ora"); const chalk = require("chalk"); // proPath 項目目錄 process.chdir(proPath); // git init const gitInitSpinner = ora( `cd ${chalk.cyan.bold(projectName)}, 執行 ${chalk.cyan.bold("git init")}` ).start(); const gitInit = exec("git init"); gitInit.on("close", code => { if (code === 0) { gitInitSpinner.color = "green"; gitInitSpinner.succeed(gitInit.stdout.read()); } else { gitInitSpinner.color = "red"; gitInitSpinner.fail(gitInit.stderr.read()); } }); // install let command = ""; if (shouldUseYarn()) { command = "yarn"; } else if (shouldUseCnpm()) { command = "cnpm install"; } else { command = "npm install"; } log(" ".padEnd(2, "\n")); const installSpinner = ora( `執行安裝項目依賴 ${chalk.cyan.bold(command)}, 需要一會兒...` ).start(); exec(command, (error, stdout, stderr) => { if (error) { installSpinner.color = "red"; installSpinner.fail(chalk.red("安裝項目依賴失敗,請自行重新安裝!")); console.log(error); } else { installSpinner.color = "green"; installSpinner.succeed("安裝成功"); log(`${stderr}${stdout}`); } });
主要的代碼就是這些,其實只要知道思路,這些東西都很簡單,雖然我寫的有點 ?:chicken:,但是主要的邏輯還是能理清楚的一些的。更加詳細的可以去:eyes:我發的源碼,多謝指教。:pray::pray::pray:
開發腳手架
因為這是小程序的腳手架,它不像其他 web
框架一樣需要很多 webpack
的配置,所以相對簡單很多。
對于這個腳手架,相比于開發者工具創建的默認項目,我彌補了它的一些問題
默認項目太過簡單,只適合自己折騰,對于團隊或者企業,缺乏相應的代碼約定/規范,沒有強制的約定會導致團隊協作間的困難,提升code review的難度,所以我在原來的基礎上加入了eslint,stylelint,prettier,commitlint等配置,以及git hook 在 pre-commit 時,執行校驗,確保提交的代碼盡量規范
由于對 css 預處理的鐘愛,另外加入了對 less 的支持,并且解決小程序背景圖不支持本地圖片的問題
由于以上基本都是文件處理,所以選擇 gulp 作為構建工具,這里是 v4, 與v3 寫法上有一定的區別,不過關系不大
在根目錄下創建 gulpfile.js
const gulp = require('gulp'); const chalk = require('chalk'); const rename = require('gulp-rename'); // 支持 less gulp.task('less', () => { return gulp .src('./miniprogram/**/*.less') .pipe(less()) .pipe(postcss()) // 配置在 post.config.js .pipe( rename((path) => { path.extname = '.wxss'; }) ) .pipe( gulp.dest((file) => { return file.base; // 原目錄 }) ); }); // 開發環境監聽 less if (env === 'development') { gulp.watch(['./miniprogram/**/*.less'], gulp.series('less')).on('change', (path) => { log(chalk.greenBright(`File ${path} was changed`)); }); } // 一下代碼注釋掉了,依賴包下載太慢了,這主要負責圖片的壓縮 const imagemin = require('gulp-imagemin'); const cache = require('gulp-cache'); // 使用緩存 gulp.task('miniimage', () => { return gulp .src('./miniprogram/**/*.{png,jpe?g,gif,svg}') .pipe( cache( imagemin([ imagemin.gifsicle({ interlaced: true }), imagemin.mozjpeg({ quality: 75, progressive: true }), imagemin.optipng({ optimizationLevel: 5 }), imagemin.svgo({ plugins: [{ removeViewBox: true }, { cleanupIDs: false }], }), ]) ) ) .pipe( gulp.dest((file) => { return file.base; // 原目錄 }) ); });
關于“Node CLI如何構建微信小程序腳手架”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。