编程知识 cdmana.com

Initial experience of template tool plop of [front end Engineering]

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

  1. install

    npm install --save-dev plop
    //npm install -g plop
     Copy code 
  2. 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 .

  3. 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 , these generator 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.

  1. 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 
  2. 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 
  3. newly build hanldebar Template file

    stay 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 
  4. 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 
  5. actual combat

    Here we can have a formal try .

    The first run npm run generator, Then enter the component name Test

    The 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

  1. 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 .

  2. 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 type action Will modify routers.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 name prompt, Modify the corresponding logic , Just add one more layer . If the routing is also divided into modules , Can cooperate with webpack Of require.context To achieve , Dynamically adjust according to the actual situation of the project .

  3. 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 code

    stay view A new template file corresponding to the above figure is added under the folder . View templates

    Add one more confirm Type of prompt, Related templates action

    //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 
  4. 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 or modify). 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 .

  5. 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

A good one plop Chinese document

Friend's Engineering Blog (react)

版权声明
本文为[White Finch]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/09/20210909135431031n.html

Scroll to Top