A friend recently made a front-end engineering sharing , I used to just taste it , After listening, I gained a lot . Considering that engineering can systematically improve their front-end capabilities , So I recently started to do this series , Starting from scratch , According to the basic structure , standard , Tools , Optimize , Automate the sequence of deployment and performance monitoring , Learning practice . Our company just asked our group to start technology sharing , I shared what I just learned plop. Share , After communication, sort it out and send it to record . In this paper, the source code
One 、plop summary
What is? plop? A micro generator framework , Used to generate template files that meet team specifications .
You can simply think of it as inquirer Dialog box and hanldebar Simple integration of templates .
Generate template file ? It sounds like and vsCode User code snippets are similar . If you only deal with single files , Then they are almost .plop Benefit from the handlebars( Chinese document ) More powerful semantic template function , Maybe slightly better than code snippets . that , Just so ? So let's try that .
Two 、plop start
-
install
npm install --save-dev plop //npm install -g plop Copy code
-
Create... In the project root folder plopfile.js
//plopfile.js module.exports = function (plop) {}; Copy code
It exports a function , This function takes in a plop Object as first argument .
-
Create generator generator
One plop Object exposes a containing
setGenerator(name, config)
Functional plop api.setGenerator
Used to create a generator (generator
). When plop Run in the terminal of the current folder or subfolder , thesegenerator
It will be displayed in the terminal in the form of a list .Here is the simplest one generator :
module.exports = function (plop) { // Create a generator plop.setGenerator('component', { description: ' Add a new public component ',// describe , What is displayed behind the generator in the terminal prompts: [], // Tips , Used to capture user input actions: [] // Behavior , Specific implementation contents }); }; Copy code
prompts That is inquirer Of prompts, May refer to
inquirer
Documents , We will not expand it in detail here .actions The standard properties of are as follows :
attribute data type The default value is describe type String action The type of ( Include add, modify, addMany force Boolean false
Enforcement ( Different action Types correspond to different logic ) data Object/Function {}
Appoint action When it is executed, it needs to be mixed with prompts The data of the answer abortOnFail Boolean true
When one action When the execution fails for some reason, all after the execution is stopped action skip Function A to specify the current action Whether the function to be executed These are plop The most basic , It's also the core content . After knowing this, we can officially start using , Come and eat !
3、 ... and 、plop First experience
Let's say vue Add a new... In the project vue File as an example to demonstrate how to use plop. Because there are many files to be used later, the default plopfile.js.
-
Create a new folder at the root of the project generators, Add a new file in the folder index.js
There are many follow-up documents , We added a new folder to store related files
//generators/index.js module.exports = (plop) => { // Create a generator plop.setGenerator('component', { description: ' Add a new public component ',// describe , What is displayed behind the generator in the terminal prompts: [], // Tips , Used to capture user input actions: [] // Behavior , Specific implementation contents }); }; Copy code
-
modify package.json
adopt --plopfile Order to designate plop The address of the entry file is just created
//package.json { //... "scripts": { //... "generator": "plop --plopfile generators/index.js", }, } Copy code
-
newly build
hanldebar
Template filestay generators New under folder index.vue.hbs file
<template> <div></div> </template> <script> export default { name: "{{ componentName }}", components: {}, props: { list: { type: Array, default: function() { return []; }, }, }, data() { return {} }, created() {}, mounted() {}, methods: {}, }; </script> <style scoped></style> Copy code
-
Perfect generator
//generators/index.js const componentExists = require('./utils/componentExists'); //componentExists It's a tool method , Used to verify whether the same name already exists module.exports = (plop) => { // Create a generator plop.setGenerator('component', { description: ' Add a new component ', prompts: [ { type: 'input', name: 'componentName', message: ' Please enter the component name :', default: 'Button', validate: value => { if (/.+/.test(value)) { return componentExists(value) ? ' The same container name or component name already exists ' : true; } return ' Component name is required '; }, }, ], actions: data => { const actions = [ { type: 'add', path: '../src/components/{{properCase componentName}}/index.vue', //componentName And prompts Of name Value correspondence , That is, enter the content for the user templateFile: './index.vue.hbs', abortOnFail: true, }, ]; return actions; }, }); }; //utils/componentExists.js /** * componentExists * * Determine whether a component or page exists */ const fs = require("fs"); const path = require("path"); const pageComponents = fs.readdirSync( path.join(__dirname, "../../src/components") ); const pageContainers = fs.readdirSync( path.join(__dirname, "../../src/views") ); const components = pageComponents.concat(pageContainers); function componentExists(comp) { return components.indexOf(comp) >= 0; } module.exports = componentExists; Copy code
-
actual combat
Here we can have a formal try .
The first run
npm run generator
, Then enter the component name TestThe creation is complete . Try again without entering the component name 、 Input empty 、 Repeat input , No problem. , There are no screenshots here. It takes up space .
That's all for today's sharing ...... How is that possible? ! Only these words are not willing to seek the far , It's clear that cv Things that can be solved . Now let's officially carry out enterprise level actual combat ( brag (= - =) ).
Four 、plop Advanced
-
What do we do when we create a new page
Generally speaking , When we add a new page, we will views/pages Create a new page folder under folder ( When the project is large, it will be divided into modules , Another layer of folders ), Then create a new page vue file , Then configure the routing , Add corresponding vuex modular , Introduce encapsulated api wait ...
Now try it plop Do these things .
First adjust the previous file structure , stay generators New under folder component Folder , Put the previous generator configuration and template files in . Add new view The folder is used to put subsequent files .generators/index.js The file is adjusted as follows :
const componentGenerator = require('./component/index.js'); const viewGenerator = require('./view/index.js'); module.exports = (plop) => { plop.setGenerator('component', componentGenerator); plop.setGenerator('view', viewGenerator); }; Copy code
stay view New under folder index.vue.hbs The file is the same as the file with the same name above .
view/index.js The file is adjusted as follows :
const componentExists = require('../utils/componentExists'); module.exports = { description: ' Add a view container ( page )', prompts: [ { type: 'input', name: 'viewName', message: ' Please enter container ( page ) name :', default: 'Form', validate: (value) => { if (/.+/.test(value)) { return componentExists(value) ? ' The same container name or component name already exists ' : true; } return ' Container name is required '; }, }, ], actions: (data) => { let actions = [ { type: 'add', path: '../src/views/{{ viewName }}/index.vue', templateFile: './view/index.vue.hbs', abortOnFail: true, }, ]; return actions; }, }; Copy code
So far, view and component The two generators have the same function .
-
Routing processing when creating a new page
src New under the directory router Folder :
//router/index.js import Vue from 'vue'; import Router from 'vue-router'; import Allrouters from './routers.js'; Vue.use(Router); export default new Router({ mode: 'hash', routes: [ // default page { path: '*', redirect: '/index' }, ...Allrouters, ], }); //router/routers.js //-- append import here -- export default [ //-- append router here -- ]; Copy code
Be careful //-- append import here -- and //-- append router here -- And below action And the regular verification in the template file .
newly build
router.js.hbs
Template file , Add two modify type action://view/router.js.hbs { path: '/{{ viewName }}', name: '{{ viewName }}', component: {{ viewName }}, }, //-- append router here -- //view/index.js ... actions: (data) => { let actions = [ { type: 'add', path: '../src/views/{{ viewName }}/index.vue', templateFile: './view/index.vue.hbs', abortOnFail: true, }, { type: 'modify', path: '../src/router/routers.js', pattern: /(\/\/-- append import here --)/gi, template: "const {{ viewName }} = () => import('../views/{{ viewName }}/index.vue');\n$1", }, { type: 'modify', path: '../src/router/routers.js', pattern: /(\/\/-- append router here --)/gi, templateFile: './view/router.js.hbs', }, ]; return actions; }, ... Copy code
Two
modify
typeaction
Will modifyrouters.js
file , Realize synchronous routing . The default here is that the page is not divided into modules , If divided into modules , Add a new... To enter the module nameprompt
, Modify the corresponding logic , Just add one more layer . If the routing is also divided into modules , Can cooperate withwebpack
Ofrequire.context
To achieve , Dynamically adjust according to the actual situation of the project . -
To configure vuex
vuex
I refer to the structure and specification modified by a friend , Not necessarily , It's best to use the one you're most familiar with . Look at the codestay view A new template file corresponding to the above figure is added under the folder . View templates
Add one more
confirm
Type ofprompt
, Related templatesaction
://view/index.js prompts: [ //... { type: 'confirm', name: 'vuex', default: true, message: ' Whether to use vuex?', }, ], actions: (data) => { let actions = [ //... ]; if (data.vuex) { let store = [ { type: 'add', path: '../src/views/{{ viewName }}/store/constants.js', templateFile: './view/constants.js.hbs', abortOnFail: true, }, { type: 'add', path: '../src/views/{{ viewName }}/store/actions.js', templateFile: './view/actions.js.hbs', abortOnFail: true, }, { type: 'add', path: '../src/views/{{ viewName }}/store/getters.js', templateFile: './view/getters.js.hbs', abortOnFail: true, }, { type: 'add', path: '../src/views/{{ viewName }}/store/mutations.js', templateFile: './view/mutations.js.hbs', abortOnFail: true, }, { type: 'add', path: '../src/views/{{ viewName }}/store/index.js', templateFile: './view/index.js.hbs', abortOnFail: true, }, ]; actions = actions.concat(store); } return actions; }, Copy code
The template file can be obtained prompts Parameters provided in , coordination handlebars Own template syntax , Realize the dynamic control template according to the user's choice , Modify the page template file
index.vue.hbs
as follows ://view/index.vue.hbs <template> <div></div> </template> <script> {{#if vuex}} import { STORE_NAME, GET_TOTAL_ACTION, SET_TOTAL_MUTATION } from './store/constants'; import storeOption from './store/index'; import { mapState, mapActions, mapMutations } from 'vuex'; {{/if}} export default { name: "{{ viewName }}", components: {}, props: { list: { type: Array, default: function() { return []; }, }, }, data() { return {}; }, computed: { {{#if vuex}} ...mapState({ total: (state) => state[STORE_NAME].total, }), {{/if}} }, {{#if vuex}} beforeCreate(){ this.$store.registerModule(STORE_NAME,storeOption)// Dynamic registration vuex modular }, {{/if}} created() {}, mounted() {}, methods: { {{#if vuex}} ...mapActions({ getToalAction: `${STORE_NAME}/${GET_TOTAL_ACTION}` }), ...mapMutations({ setToalMutation: `${STORE_NAME}/${SET_TOTAL_MUTATION}`, }), {{/if}} }, {{#if vuex}} destroyed(){ this.$store.unregisterModule(STORE_NAME,storeOption)// Uninstall when leaving the page vuex modular , Otherwise, go to the page again vuex Of action It will trigger many times }, {{/if}} }; </script> <style scoped></style> Copy code
-
Is this ?
It seems that I feel just ... Um. , ok ?
Think about it carefully. The routing file we modified is probably a public file , Other colleagues may also add and modify , In order to avoid conflict , It's best to push to... Immediately after creating a new page git. Why ,node You can execute script commands , Can you put git The operation is also handled ? Try it .
Import exec, Customize polp Of action:
//generator/index.js const { execSync } = require('child_process'); //node Can be inherited by child processes. , execSync yes exec Synchronized version of , derivative shell And in time shell Run command in const componentGenerator = require('./component/index.js'); const viewGenerator = require('./view/index.js'); module.exports = (plop) => { plop.setGenerator('component', componentGenerator); plop.setGenerator('view', viewGenerator); plop.setActionType('git', (answers, config) => { try { execSync(`git pull`); execSync(`git add .`); execSync(`git commit -m " newly build ${config.file}"`); execSync(`git push`);// These are just examples , It doesn't mean the truth } catch (err) { throw err; } }); }; //view/index.js actions.push({ type: 'git', viewName: data.viewName, }); Copy code
setActionType Method can be customized actions( Be similar to
add
ormodify
). The default parameters are as follows :Parameters type describe answers Object generator prompts The answer config ActionConfig generator action Configuration objects for plop PlopfileApi be used for action Where plop Of documents plop api thus it can be seen ,plop Or say node Can do anything we want him to do .
-
other
The previous steps have realized the basic routing ,vuex To configure ,git Submit . But there must be more common configurations to be added in real projects , For example, the pages of our project have a unified basic layout , That template is rich again :
//view/index.vue.hbs <template> <div class="pagesDiv"> <page-little-menu :title="[ { name: ' Data governance ' }, { name: ' Problem device view ', path: {{ viewName }} }, ]" /> <div class="tableDivForNoCondition"> </div> </div> </template> Copy code
For another example, we have dynamic computing el The table height is then adaptive mixins, If there are pages with tables, you can optionally inject templates ;
General pages are component-based development , Create a new page under the page folder components Folder , Also add a sample component , And then in
index.vue
Importing components in is also essential ;Packaged
axios/fetch
etc.api
It's also to be copied ...Wait too much , I won't go into details here . I think every old front end er There are new , newly build , newly build ,CV,CV,CV,CV Then the experience of setting up the page infrastructure . While using plop After that, just enter the page name , Select some configurations , A page structure that conforms to its own project specifications is built , It's great to think about it .plop also helper,partial Other auxiliary functions , Interested friends, don't hesitate , You start too .
5、 ... and 、 summary
The above implements a template file from the most basic configuration , To a skeleton rich ( Humble , But I don't want to write here , The following contents are similar ) View container generation . Compared with its efficiency improvement , In fact, the author is more concerned about its role in the promotion of norms . Using it can keep the whole project consistent , View 、 data 、 The finer the tools are split , The more projects are written by the same person . This is too important for multi person collaboration , When you modify or take over a colleague's code , Can help you quickly adapt to the code , Where the problem may occur or where you will modify it , You know everything . But when the project or team is not big enough , The finer the split , The more difficult it is to write , For example, above vuex structure , May be added every time , To modify, you need to modify multiple files . And merge files , Cancel constant setting , If you simplify the structure , Probably plop Efficiency improvement is better than standard improvement .
The vision of this article is limited to the page level , But zoom in , Write in the configuration of the whole project , Upload again npm
, Then it's a scaffold ? Try it when this series is finished .
Reference resources :