1、如何封装一个模版式的简易脚手架?
1.1 了解并安装 yeoman-generator
Generators 是yeoman生态系统的积木,是通过yo命令运行而为终端用户生产文件的插件
1.2 准备一个打包项目模板,无关业务的
例如
webpack|--build # webpack配置文件|--config # webpack可配置选项|--bankConfig # 从src中抽离出来的可配置选项|--bisConfig # 业务配置|--routeConfig.js # 路由配置|--customSrc # 基于src的定制目录|--src # 项目的开发目录|--assets # 项目的静态资源,参与webpack的编译|--external # 项目的外部资源,不参与webpack的编译|--lib # 项目的公共脚本|--BaseApi.ts # 公共请求|--BaseStorage.ts # 公共存储|--CommonRouter.ts # 公共路由|--CommonStore.ts # 公共状态|--extend.js # 扩展方法|--components # 项目的公共组件|--model # 项目的数据模型|--User # 用户类|--System # 系统类|--Utils # 工具类|--service # 项目的接口交互|--ApiUrl.ts # 接口路径|--User.ts # 用户接口|--Upload.ts # 文件上传接口|--mock # 项目的虚拟接口数据|--pages # 项目的页面模块,每个子目录对应一个vue单页面|--.html # 模板文件|--.js # 入口文件|--.less # 根样式|--.vue # 根组件|--router # 路由配置|--views # 路由组件|--store # 状态管理|--components # 复用组件|--shims-vue.d.ts # TypeScript识别.vue文件|--shims-tsx.d.ts # 允许在vue中编写.tsx文件|--package.json # 项目配置文件,包含项目的名称、版本号、命令行、第三方依赖等|--tsconfig.json # typescript配置文件|--.babelrc.js # babel配置文件|--.eslintrc.js # eslint配置文件|--.eslintignore # eslint忽略的文件列表|--.gitignore # git忽略的文件列表|--postcss.config.js # postcsss配置文件|--.editorconfig # 编辑器配置文件|--INSTALL.txt # 项目安装说明|--CHANGELOG.txt # 项目版本日志|--make-package.sh # 项目自动化打包|--deploy.sh # 项目自动化部署
1.3 初始化脚手架的 pageage.json
需要注意的点:
1、脚手架的 name 必须以 generator- 开头
2、keywords 属性必须包含 "yeoman-generator"
{"name": "generator-xxx","version": "0.0.1","description": "简易脚手架","scripts": {},"author": "xxxx","private": false,"license": "ISC","publishConfig": {"registry": "http://xxxxxx" // 私库/脚手架发布地址},"files": ["app" // 属性必须是一个在你的generator里面用到的文件和目录的数组。],"keywords": ["yeoman-generator"],"dependencies": {"yeoman-generator": "^3.2.0"}}
1.4 利用 yeoman-generator 编写脚手架生成脚本
1.4.1 在 pageage.json 里 files 定义的目录下 新建 index.js 入口文件 ,入口文件得导出一个 generator 继承
1.4.2 ye 执行顺序为 initializing -> prompting ->configuring -> default -> writing-> conflicts -> install -> end
initializing -- 初始化方法(检查状态、获取配置等)prompting -- 获取用户交互数据(this.prompt())configuring -- 编辑和配置项目的配置文件default -- 如果generator内部还有不符合任意一个任务队列任务名的方法,将会被放在default这个任务下进行运行writing -- 填充预置模板conflicts -- 处理冲突(仅限内部使用)install -- 进行依赖的安装(eg:npm,bower)end -- 最后调用,做一些clean工作
1.4.3 完整示例:
/** @Date: 2021-01-22 10:19:10* @LastEditTime: 2021-02-07 15:42:37* @Description: 脚手架生成器*/const path = require("path");const fs = require("fs");const mkdirp = require("mkdirp");const Generator = require("yeoman-generator");module.exports = class extends Generator {// 初始化initializing() {// 项目属性的默认值this.props = {name: '',version: '0.0.1',description: '',author: ''};}// 接收用户输入prompting() {// 问题const questions = [{type: "input",name: "name",message: "请输入项目的英文名",},{type: "input",name: "version",message: "请输入项目的版本号",default: this.props.version},{type: "input",name: "description",message: "请输入项目的描述信息",},{type: "input",name: "author",message: "请输入项目的作者名称",}]// 根据回答,生成新的项目属性return this.prompt(questions).then((answers) => {const { name } = answers;if (!name) {throw new Error('项目名称不能为空');} else {const fileList = fs.readdirSync('./');if (fileList.find(e => e == name)) {throw new Error('当前目录下存在同名项目');} else {this.props = answers;}}});}// 创建项目目录default() {const { name } = this.props;if (path.basename(this.destinationPath()) !== name) {mkdirp(name);this.destinationRoot(this.destinationPath(name));}}// 写入项目文件writing() {// 将模板文件复制到输出目录,并替换文件中的变量this.fs.copyTpl(this.templatePath("project/"),this.destinationPath(""),this.props,{},{globOptions: { dot: true }});// 重命名文件,去掉下划线前缀this.fs.move(this.destinationPath("_package.json"),this.destinationPath("package.json"),);}}
2、基于 element-ui 封装组件遇到的问题
2.1 dialog 二次封装 出现 弹出又关闭的情况
场景:dialog 组件需要根据对应的元素定位,并不是传统意义上的在整个屏幕居中展示
思路:
1、dialog 基于那个对应元素定位。也就是改变dialog最外层元素的定位方式
.el-dialog__wrapper{position: absolute;}
2、由于 定位方式由 fixed 变成了 absolute 那最外层元素 el-dialog__wrapper 宽高都为 0,所以必须根据内部 宽高进行动态计算然后赋值给 el-dialog__wrapper
实现:
// dialog 打开回调立马计算弹框内容宽高opened() {this.getDialogStyle();},getDialogStyle() {this.$nextTick(() => {const searchBox = this.$refs.SearchBox;if (!searchBox) return;const { width, height } = searchBox.getBoundingClientRect();this.dialogStyle = {width: width + 50 + "px",height: height + 50 + "px",zIndex: 2500,};});},// 将 dialogStyle 绑定在 dialog 最外层 el-dialog__wrapper 元素中
3、给 body 绑定事件 然后点击 body 关闭弹框
document.body.addEventListener("click", (event) => {this.isShow = false;event.stopPropagation();});
遇到的问题:
1、点击按钮打开弹框,打开之后立马又关闭了
分析:应该是点击按钮的时候,由按钮点击 冒泡到 body 上,点击打开弹框的同时,也触发了 body 上的点击事件,触发了 弹框的关闭通知
尝试解决方法一:给使用的页面,有绑定点击事件的地方,添加阻止冒泡的方法,但是页面上绑定点击的地方太多了,总有漏掉的地方,这始终是个隐患。所以这种方式不得不舍弃掉
尝试解决办法二:通过伪元素,在 dialog 内容区域外层加一个 mask 遮罩 这样,点击 dialog 内容外层 ,其实跟之前一样,还是点击在 dialog 内部 ,这样就不用特意添加点击事件,防止冒泡事件的问题了
.el-dialog__wrapper{&::before {content: "";position: fixed;width: 100vw;height: 100vh;top: 0;right: 0;bottom: 0;left: 0;overflow: auto;margin: 0;}}
