赞
踩
先介绍一下这个系类(tinymce),该系列以后相关的代码都会更新在 tinymce-plugins
tinymce 是一个富文本插件。官网已经介绍的很清楚了
这个系列主要的目的就是把在工作中遇到的 tinymce 遇到的坑和一些插件开发流程记录下来(毕竟网上这部分资料真的太少了。。。)
tinymce 使用是非常简单的,官网也有很详细的介绍(一些简单非必要的配置我就不细说了)
这里的环境搭建并不是 tinymce 的源码环境或者只是单纯运行 tinymce,而是为了开发 tinymce 配套插件
准备的环境。所以就从环境搭建先开始吧
之前尝试过用 webpack。失败了,或许是实力不够把~
所以下面会介绍使用 rollup 来搭建:rollup 源文档、rollup 中文文档
接下来使用的版本是 tinymce@5.2.0
注意下载的是 dev 版本,因为后续有看源码的需要~
下载了 Dev 的代码后,源码部分都在 modules
文件夹里面的,搭建环境要用的 JS 隐藏的比较深,找到有 tinymce.min.js 的文件夹的上一层。就是我们要引入的 JS 了
目录结构如下:红色框中的就是 tinymce 的代码,剩下的我们自己新建一个 index.html
如果需要配置 language ,需要自己下载对应的中文包,我这里就不管他了。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>tinymce 插件开发环境</title> <script src="./js/tinymce/tinymce.min.js"></script> </head> <body> <textarea class="tinymce" style="height:90vh;" id="tinymce"> </textarea> </body> <script> tinymce.init({ selector: 'textarea.tinymce', plugins: 'code advlist lists advlist', toolbar: 'undo redo | advlist fontsizeselect lineheight', setup(editor) {} }) </script> </html>
这时候直接打开 index.html 或者用 http-server 运行可以看到如下:
webpack 和 rollup 都是打包工具,打包配置并不难。
可是每次看完教程自己打包都会踩坑,那是因为每个时期的依赖包版本的问题 = =。 这才是打包的最大障碍
添加 2 个运行命令,用于运行和打包的。
rollup 号称是无须配置文件都可以打包的,具体可以看下官网 rollup 命令
这里我就只在 serve 环境下使用 -c
和 --watch
功能。
-c 可以指定配置文件入口。–watch 监听文件变化
从运行命令看得出,我们要建一个 build/rollup.config.js
为了减少踩坑过程,直接贴出我的依赖包配置(尤其是 babel)
{ "scripts": { "serve": "cross-env NODE_ENV=development rollup -c build/rollup.config.js --watch", "build": "cross-env NODE_ENV=production rollup -c build/rollup.config.js" }, "devDependencies": { "babel-preset-es2015-rollup": "^3.0.0", "babel-preset-stage-0": "^6.22.0", "cross-env": "^7.0.3", "less": "^4.1.2", "livereload": "^0.9.3", "postcss": "^8.3.9", "rollup": "^2.58.0", "rollup-plugin-babel": "^2.7.1", "rollup-plugin-clear": "^2.0.7", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-copy": "^3.4.0", "rollup-plugin-livereload": "^2.0.5", "rollup-plugin-postcss": "^4.0.1", "rollup-plugin-serve": "^1.1.0", "rollup-plugin-uglify": "^6.0.4", "rollup-watch": "^3.2.2" } }
这个配置要达到什么效果?
多入口多出口
的打包需求.min.js
文件。所以要有一个压缩 JS 的功能.less
根据 tinymce 的插件目录要求
js/tinymce/plugins/
下plugin.js
和 plugin.min.js
。tinymce 会自动选择是加载 .js 或 .min.js。所以测试插件目录如下:
使用的话打开 index.html。在初始化 tinymce 的代码中改为如下(在 plugins 加入 myplugins myplugins2):
tinymce.init({
selector: 'textarea.tinymce',
plugins: 'code advlist lists advlist myplugins myplugins2',
toolbar: 'undo redo | advlist fontsizeselect lineheight'
})
看到插件有正常加载
然后就是 rollup.config.js 的配置了
配置的雏形,因为是多入口多出口,所以只能导出一个数组的配置了
format 需要提一下就是打包为iife
,就是立即执行函数。等下就能看到效果了
其他输出的类型:(amd, cjs, esm, iife, umd)
当然还有其他的配置,
const path = require('path') const pluginsPath = path.resolve(__dirname, '../src/js/tinymce/plugins/') export default [ { input: path.join(pluginsPath, 'myplugins/plugin.js'), output: { format: 'iife', file: path.join(pluginsPath, 'myplugins/plugin.min.js') } }, { input: path.join(pluginsPath, 'myplugins2/plugin.js'), output: { format: 'iife', file: path.join(pluginsPath, 'myplugins2/plugin.min.js') } } ]
然后运行npm run serve
,来启动这份配置。看到下面的效果就是生效了
上面的效果可以看出 format 已经生效了,把我们的函数打包为立即执行函数。但也真的仅仅是打包为立即执行函数,如果我们写一些 ES6 语法,并不会转换
接下来用到 2 个库(如果上面和我一样已经直接配置好 package.json 的就不用重复安装了)
rollup-plugin-babel
ES6 转 ES5 插件
babel-preset-stage-0
babel-preset-es2015-rollup
rollup-plugin-commonjs
将 CommonJS 模块转换成 ES6,防止他们在 Rollup 中失效;
import babel from 'rollup-plugin-babel' // ES6转ES5插件 import commonjs from 'rollup-plugin-commonjs' // 将CommonJS模块转换成ES6,防止他们在Rollup中失效; export default [ { input: path.join(pluginsPath, 'myplugins/plugin.js'), output: { format: 'iife', file: path.join(pluginsPath, 'myplugins/plugin.min.js') }, plugins: [ // 添加插件配置 commonjs(), // es6 转 babel({ babelrc: false, //不设置.babelrc文件; exclude: 'node_modules/**', // 忽略 node_modules 目录(虽然我们也用不上 node_modules) presets: ['es2015-rollup', 'stage-0'], //转ES5的插件; plugins: ['transform-class-properties'] //转换静态类属性以及属性声明的属性初始化语法 }) ] } // .... ]
重新运行,发现语法已经有转义了
既然是 .min.js
那压缩少不了
涉及的插件包就是 rollup-plugin-uglify
import { uglify } from 'rollup-plugin-uglify' // js压缩
export default [
{
// ...
plugins: [
// ...
uglify()
]
}
]
是不是感觉配置特别简单?!就引入插件,在 plugins 目录执行方法就行了
rollup-plugin-uglify 注意下版本的问题,就版本引入是 import uglify from 'rollup-plugin-uglify'
。如果用的是我的版本的话,需要通过解构赋值来引入。
重新运行就能看到效果了
注意有些包不一定是要引入的,比如 livereload
就不需要引入,不过 rollup-plugin-livereload
依赖了这个包,所以才需要装
关于 serve 的配置,可以看下: npm - rollup-plugin-server
import serve from 'rollup-plugin-serve' // serve服务 import livereload from 'rollup-plugin-livereload' // 热更新 // 引入一个新变量 const srcPath = path.resolve(__dirname, '../src/') export default [ { // ... plugins: [ // ... serve({ open: true, // 自动打开浏览器 verbose: true, // 在终端输出打开的链接 contentBase: srcPath, // 项目入口 openPage: '/index.html', // 默认打开的页面 port: '8080' // 端口号 }), livereload() ] }, { // ... plugins: [livereload()] // 注意 serve 只要执行一次 } ]
server 只需要执行一次,livereload 需要有多少个入口执行多少次
因为 server 启动一个就够了,而多入口监听文件变化,热更新是依赖 livereload 的插件的
看到有自动打开浏览器,那就是配置对了!
看下 plugins.min.js 文件,插件帮我们加了一份热更新代码。
热更新效果 虽然是强制刷新而不是局部更新,可是那也很香了好吗~
不要问为什么不用 scss,其实都是一个道理的
需要的插件包:
import postcss from 'rollup-plugin-postcss' export default [ { plugins: [ postcss({ extensions: ['.less'], // 编译.less 文件 extract: true, modules: false, inject: false, // 核心,不要在 html 插入css代码,因为 tinymce 有自己引入css的一套方法 minimize: true }) // ... ] } ]
注意引入顺序
如果我们先处理 js ,然后在处理 css 就会报错。所以我们处理 less 应该提到 plugins 的第一位
还有一个要注意点的是,less 文件必须在 plugin.js 引入,否则 rollup 是不会帮你打包对应的 css 文件的
其次就是,不管你的 less 文件有多少个,因为 output 配置的输出名称是是 plugin.min.js。所以 css 最后都会被合并到 plugin.min.css
可以看到现在是写了 .less 文件完全没反应的,接下来在 plugin.js 引入一下 less import './style/index.less'
就能看到同级目录输出 plugin.min.css
了
至此,基本骨架已经有了。不过上面的配置都是运行 serve 的。我们打包正式的包,肯定不能有热更新之类的代码了,所以针对 npm run build
在做一些修改
cross-env 在 package.json 文件就有设置了 NODE_ENV
所以我们判断是开发环境/打包只需要判断
const isDev = process.env.NODE_ENV == 'development'
在开发目录 /src/js/tinymce/plugins
里面可能会有很多无关的文件,所以打包我们还需要打包一份到 /dist
目录,方便我们发布版本
对于这种一个文件对应多个出口的,output 支持配置为数组!
当然了,这部分只是伪代码,还需要把 serve 注释掉
const rootPath = path.resolve(__dirname, '../') const pluginsPath = path.resolve(__dirname, '../src/js/tinymce/plugins/') const isDev = process.env.NODE_ENV == 'development' export default [ { input: path.join(pluginsPath, 'myplugins/plugin.js'), output: [ { format: 'iife', file: path.join(pluginsPath, 'myplugins/plugin.min.js') }, { format: 'iife', file: path.join(rootPath, 'dist/myplugins/plugin.min.js') }, { format: 'iife', file: path.join(rootPath, 'dist/myplugins/plugin.js') } ] } ]
打包后的效果如下(有几个小问题,样式打包多了,plugin.js 并非未压缩的代码,后面的配置还会继续完善):
动态入口其实就是指定目录后,遍历文件夹,然后根据我们的规则生成对应的 input
和 output
就行了
包括之前的一些代码也重新整理下,整理为如下几个:
const path = require('path') const fs = require('fs') module.exports = function(entry) { if (!entry) { console.log('入口目录不存在', entry) process.exit(0) } try { let stat = fs.statSync(entry) if (!stat.isDirectory()) { console.log('入口目录不存在', entry) process.exit(0) } } catch (e) { console.log('入口目录不存在', entry) process.exit(0) } let routes = [] fs.readdirSync(entry).forEach(function(item, index) { let pluginFolder = path.join(entry, item) let floderStat = fs.statSync(pluginFolder) // 这里判断是否插件目录 if (floderStat.isDirectory()) { try { let plugin = path.join(pluginFolder, 'plugin.js') let pluginStat = fs.statSync(plugin) if (pluginStat && pluginStat.isFile()) { routes.push({ path: plugin, floder: pluginFolder, name: item }) } } catch (e) {} } }) return routes }
const babel = require('rollup-plugin-babel') // ES6转ES5插件 const commonjs = require('rollup-plugin-commonjs') // 将CommonJS模块转换成ES6,防止他们在Rollup中失效; const { uglify } = require('rollup-plugin-uglify') // js压缩 const serve = require('rollup-plugin-serve') // serve服务 const livereload = require('rollup-plugin-livereload') // 热更新 const postcss = require('rollup-plugin-postcss') /** * 获取不同配置对应的插件 * @param {*} isDev 是否开发环境 * @param {*} isFirst 是否第一个入口 * @param {*} config 拓展配置 * @returns */ module.exports = function(isDev, isFirst, config = {}) { let plugins = [ postcss({ extensions: ['.less'], // 编译.less 文件 extract: true, modules: false, inject: false, // 核心,不要在 html 插入css代码,因为 tinymce 有自己引入css的一套方法 minimize: !config.unUglify }), commonjs(), babel({ babelrc: false, //不设置.babelrc文件; exclude: 'node_modules/**', presets: ['es2015-rollup', 'stage-0'], //转ES5的插件; plugins: ['transform-class-properties'] //转换静态类属性以及属性声明的属性初始化语法 }) ] if (isDev) { // 只有第一个才需要serve isFirst && plugins.push( serve({ open: true, // 自动打开浏览器 verbose: true, // 在终端输出打开的链接 contentBase: config.srcPath || '', // 项目入口 openPage: '/index.html', // 默认打开的页面 port: '8080' // 端口号 }) ) // dev 都加热更新 plugins.push(livereload()) } else if (!config.unUglify) { // 打包才压缩js plugins.push(uglify()) } return plugins }
const path = require('path') const rootPath = path.resolve(__dirname, '../') const pluginsPath = path.resolve(__dirname, '../src/js/tinymce/plugins/') const srcPath = path.resolve(__dirname, '../src/') const isDev = process.env.NODE_ENV == 'development' const getEntry = require('./getEntry') const getPlugins = require('./getPlugins') function run() { let config = [] let isFirst = true getEntry(pluginsPath).forEach(item => { let _output = [ { format: 'iife', file: path.join(item.floder, 'plugin.min.js') // 基础目录,打包在 plugins } ] if (!isDev) { _output.push({ format: 'iife', file: path.join(rootPath, 'dist', item.name, 'plugin.min.js') // 打包目录 }) } config.push({ input: item.path, output: _output, plugins: getPlugins(isDev, isFirst, { srcPath }) }) !isDev && config.push({ input: item.path, output: { format: 'iife', file: path.join(rootPath, 'dist', item.name, 'plugin.js') // 打包一个不压缩的 }, plugins: getPlugins(isDev, isFirst, { srcPath, unUglify: true }) }) isFirst = false }) return config } export default run()
如果不区分目录,那么 plugins 下内置的插件也是我们的打包对象,显然我们并不用关心这部分代码,所以加一个配置来过滤掉这部分的文件
所以我采用了方式 2,改一下 build/getEntry.js
改了前缀名的话,相关的引入记得也改一下~
最后发现一个小问题就是打包 less 的时候并没有帮我们把图片资源打包进入
所以在约定一个:插件下的 assets
目录,虽然不参与打包,但是打包的时候直接把 assets 目录复制一份。这时候引发另外一个问题就是打包后的 less,层级没有自动帮我们改过来,比如下面的情况:
这种情况,只能约定大于配置了~写好文档,css 文件都写插件根目录,然后引入对应 assets 资源。不然这种情况太极端了 (就是懒)
要用到的插件包
rollup-plugin-clear
rollup-plugin-copy
修改 build/getEntry.js 判断目录是否需要复制 assets
修改 build/getPlugins.js 引入 clear 和 copy 插件
代码就不贴了,看下 gitee 把 tinymce-plugins
至此 tinymce 系列的第一章 tinymce 环境搭建,就搭建好了
以后开发 tinymce 插件也是基于这个环境了~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。