preface

First of all, thank you very much for the support of the first article, in the end of this article will be used for some key to do a detailed description, here will first bring our second article vuE3 project practice (source code at the back of the oh), for those who have not read the first article we still put on the overall introduction of the article

This is a continuous article, divided into the following stages, each stage will produce an article (this is the second section) :

  • Introduction and use of the entire project process and achieved and unachieved goals
  • Overall analysis of vue3.0 used in the front end and what pits we will use in the future
  • How does the NodeJS backend do strong type qualification like Java springMVC
  • How to realize automatic cluster deployment of the whole project on small server
This article is quite literal and takes some time to understand and digest. It mainly analyzes logical aspects such as design patterns and code structures (although mine may not be correct, I think it will be useful in a particular project).

Vue3 is quick to get started

You may have read countless articles about VUE3, in this place I need to say a little different, I will talk about some points and problems when I use

If you want to do a good job, you must sharpen your tools first. If you are not familiar with VUE3, you can mainly understand two things:

  • What is composition-API, and what was the magic that made Vue transform
  • Vue3 API is different, click me to open API

After understanding, I will introduce my understanding of some new writing methods:

setup

A simple, crude function that covers everything and lets us start our commination-API, so what do we need to pay attention to?

  • First, as a function, vue gets data or methods and writes them to return.
  • Everything written inside the function corresponds to the previous CREATE life cycle
  • Inside the function we have constants and so on, so that the data in the response needs to be reactive or refref (in VUe3 it is manually imported).
  • We can’t use this (for undefind) inside a function like vue2, so we can’t use the extra arguments directly to this.xxx
  • Changes to life cycle usage specific view API (can write multiple sequentially execute to comply with eventLoop rules)

Watch and watchEffect

Listening in VUE3 gives us two choices, so what’s the difference between them?

watch:

  • The watch object must be: A watch source can only be A getter/effect function, A ref, A reactive object, or an array of these types, So we want to watch some data directly that doesn’t work.
  • Data changes before and after monitoring
  • Initialization execution or not can be set

watchEffect:

  • Automatically collects internal response data to change execution
  • Initialization is performed once
  • After collecting dependencies, effect fires, noting logical recursion, and internal value changes continue to fire

This makes the watchEffect look good, regardless of the trigger parameters. In fact, when the use of special want to be able to exclude trigger parameters, or did not notice how to trigger so many times, or direct logic confusion.

Therefore, IF it is a complicated function, I recommend you to use Watch to manually trigger it as much as possible to prevent some problems caused by ghost. (But watchEffect is really handy.)

Ref and reactvie

Both are computed properties, one for an object and one for a single value, so what are the problems to avoid when using them?

ref:

  • The ref value is processed as an object, so you need xxxx.value in setup, but not in HTML code templates!!
  • If a value is bound to reactive after the object is refed, then reative object access does not require.value
  • Note: If the ref value is directly equal to the assignment value, the page of the direct assignment in create will still display normally. But later operations will lose the response, it is very difficult to find the bug how to lose the response.
  • Ref contains the refs method, which is implemented by defining a ref object with the same name in the DOM and return. Vue assigns the index to the object with the same name through rendering

Reactive:

  • All internal parameters are added to the response, but a parameter can be eliminated in response
  • As with ref, do not directly equal the object after reative, otherwise the object response will be lost
  • Some of you might think that you have lost your response, so why don’t YOU just reative again? You can’t do that, because you are binding to the template of the page to the previous object, you change the object but the page does not change, so the previous content is displayed.

computed

The method that returns data in VUe2 returns a ref object in vue3. We’ve also seen the API say this: Pass in a getter function that returns a ref object that cannot be manually modified by default. It’s a ref object that fires the getter when we get it, and of course we can write the computed property that way

const HOT_HTML = computed({ get: () => store.state.themes.HOT_HTML, set: () => { store.commit('CHANGE_HOT_HTML'); }});Copy the code

You can see how we can manipulate it freely, and then I think that’s the reactive principle, and I’ll rewrite get and set.

Props, emit

So how do I get some common methods and pass parameters? The simple thing is to change the way we get it. When we execute the setup function, we pass some parameters that we can use, and these are some of the common ways, as written below

setup(props, { emit }) {
...
}
Copy the code

Practice a wave of code

Warning, there’s a lot of code coming in

<script> import { computed, reactive, ref } from 'vue'; import { useStore } from 'vuex'; import router from '@/router/index'; import MessageBox from '.. /.. /.. /components/popUps/MessageBox'; import Show from '.. /.. /.. /api/Show'; import SaoSelect from '.. /.. /.. /components/select/SaoSelect'; export default { name: 'Home', components: { SaoSelect, MessageBox }, props: { scrollTop: { type: Number, default: null } }, setup(props, { emit }) { const store = useStore(); const messageDia = ref(null); / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * block control * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * *************************************************************************************************/ const cartList = computed(() => store.state.cart.cartList); const showObj = reactive({ showClose: false, list: [] }); / / to get basic data function getAllComponentsWithHtml (classify) {Show. GetAllComponentsWithHtml ({the classify: the classify, pageSize: 6 }).then((response) => { showObj.showClose = true; setTimeout(() => { showObj.list = response.list; showObj.showClose = false; }, 1200); }); } getAllComponentsWithHtml(); function showBigHtml(index) { const changeDom = document.getElementsByClassName('code-open-iframe')[index]; if (showObj.list[index].showBig) { showObj.list[index].showBig = false; changeDom.style.position = ''; changeDom.style.left = ''; changeDom.style.top = ''; changeDom.style.width = ''; changeDom.style.height = ''; changeDom.style.zIndex = ''; } else { showObj.list[index].showBig = true; changeDom.style.position = 'fixed'; changeDom.style.left = '0'; changeDom.style.top = '0'; changeDom.style.width = '100%'; changeDom.style.height = '100%'; changeDom.style.zIndex = '1'; } } function changeRouter(id) { const openUrl = router.resolve({ path: `/Code/index`, query: { id: id }}); window.open(openUrl.href, '_blank'); } function addCart(item) { if (! item.cartMove) { item.cartMove = true; store.commit('changeCartList', item); setTimeout(() => { item.cartMove = false; }, 900); } else {messageDia. Value. ShowMessage (' error ', 'please don't frequent operation oh'); }} / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * search control * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * *************************************************************************************************/ const selectObj = reactive({ inputText: '', classifyList: [], chooseClassify: null }); Then ((response) => {selectObj. ClassifyList => Response.map ((item) => ({value: item.id, name: item.name })); SelectObj. ClassifyList. Unshift ({value: null, name: 'all'}); }); / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * the right function * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * *************************************************************************************************/ function openCartDia() { store.commit('changeCartDiaShow'); } function backToTop() { emit('backtop'); } function gotoSearch(params) { emit('backtop'); router.push({ path: '/Show/list', query: params }); } return { selectObj, showObj, messageDia, cartList, showBigHtml, changeRouter, addCart, getAllComponentsWithHtml, openCartDia, backToTop, gotoSearch }; }}; </script>Copy the code

This nearly 200 lines of code is the first page we see, you can take a look at what is different from the usual VUE?

  • A lot of things to import, if you are not familiar with it still need to look it up
  • Since code can conform to comcomposition API, can our comments also be differentiated by module
  • The return feels a bit cluttered, and WebStorm is not able to access the method directly by clicking on the template, but rather to access the place where the return is
  • Getting the DOM of the ref is a little tricky
  • Teleport tag! Internal template code can be directly added to the specified DOM, easy to steal! (The code here does not)
These are some of my usual summary of vue2 to vue3. Of course, I think the specific use of these are just oral, in fact, only when using can I really feel the difference, when encountering problems will I deeply realize these differences.


Then I will introduce the content of the whole project

Overall Project Structure

A good project structure can make each function module in our project clear, and even understand part of the code content. I think this is a problem that needs in-depth discussion. Here I just talk about my understanding of the structure of this project.

File infrastructure

In fact, the file structure is the same as most VUE projects, the main body of the following structure

-public -ace -styles -iconfont -index.html - src-API - Assets - Components -decorators -router - Store (vuex) -types (.d.ts files) -utils (Common configuration and toolbar) -views - app.vue (entry vue) -main.ts (main entry file) -tests ...Copy the code
  • Some of this is explained in detail after the Decorators folder
  • All configurations in the project are written by TS, and all VUE files are normal JS (ts is used for project foundation, JS is used for specific business rapid development).
  • The content in public is content that does not need to be packaged. Some tripartite plug-ins have problems when packaged, so they are introduced in this way

Style design structure

Not know everybody experience not, actually, the whole page in the editor can support change skin, I think for general change skin design more conditions are introduced using CSS file switch the style, but I think to do so to write efficiency and rapid new themes are not particularly friendly, At this point, we can use our functional style to make some modifications.

Instead of taking a look at the code that intercepts a style function:

/ / color function. A mixin - the font, color (@ name) {each (@ themeList, {@ fc: ~ '@ {name} - @ {value}'; [data-theme='@{value}'] & { color: @@fc; }}); }Copy the code

The great thing about this is that when we use styles, we don’t write specific color styles, we just write a proxy function, and then we implement the real style through concatenation of style names, so that we can create countless theme styles in one write. The sorting process is as follows:

Interface Design Architecture

Interface design is related to the decorator, so it is more convenient to explain the idea of an overall flow chart first

Does it look like a line, one more step than the axios Dec decorator we normally write, so what does the decorator do? Let’s look at the contents of one decorator:

static Post(url: string): PropertyDecorator { return (target: any, propertyKey: string | symbol): void => { target[propertyKey] = (params: any, config? : object) => Request.post(url, params, config); }; }Copy the code

This is just a simple way to refactor the original method to make the request, and by doing so our API files can be written very, very simply, like this:

export default class Code {
   @AxiosDec.Post('/code/codeOnline/setHtml')
   static setHtml: any

   @AxiosDec.Post('/code/CodeControl/getTemplate')
   static getTemplate: any

   @AxiosDec.Post('/code/CodeControl/saveHtmlTemplate')
   static saveTemplate: any

   @AxiosDec.Post('/code/CodeControl/saveCodeTemplate')
   static saveCodeTemplate: any

   @AxiosDec.Post('/code/CodeControl/delectCodeById')
   static delectCodeById: any

   @AxiosDec.Post('/code/CodeControl/getAllComponents')
   static getAllComponents: any
}
Copy the code

Of course I personally feel very comfortable, in fact, the whole is not much use

Component Design Architecture

It needs to be explained here that this architecture is only for components with value binding, which is the basic component of OUR UI development, because the project itself does not use any three-party content, so it has a component architecture

One, check structure

Whether you when doing the project has met the form is just a simple input box to do input validation, this time with a lot of our framework dojo.provide and inject into the form validation, not directly provide interfaces for us to check that we are unable to carry out immediate check for single input box. I designed the overall flow logic of validation (which feels like Angular validation mode) to address this problem.

The overall logic is the following structure. Because the overall implementation content is too much, it is not put in the section of architecture for sorting out, and put in the section of introducing important functions.

2. Promise popup

When we submit some forms, we often encounter the situation that we need to open a popover and then continue operations after obtaining information. The common way is to use the value passed by the component to control the popover display, trigger emit and continue the method after completion.

The process is the same as a Promise. Resolve is triggered to proceed. Can we wrap the popover as a large Promise? And internal content customization with Solt parameters to complete a high degree of liberalization.

The overall flow is shown in the figure above, which is implemented by using the Promise feature to throw resolve and Reject to external methods.

Function diaPromise() {diashow. value = true; return new Promise((resolve, reject) => { stepPromise.resolve = resolve; stepPromise.reject = reject; }); }Copy the code

This is probably enough for some of the main structure introductions, and much of what is not covered is the normal development process and architecture.

The specific implementation

Online compilation

Have you seen how vUE for online editing is implemented before? I think there are two main ways to achieve this:

  • Online editing and mounting of code by using Vue’s $mount is purely front-end
  • This is done by passing code to the back end using iframe, presenting blocks of content as iframe web pages

However, for a website project, especially this kind of website that needs to be added in the later period, the operation after the back end is undoubtedly a lot of benefits:

  • Able to record history, record history code
  • Realize memory function
  • Statistical code module
  • Highly custom code handling
  • Independent web environment, no variable impact
  • User Operation Records

So how does this website come true specifically? Or the old method on the flow chart:We’ll talk about the front end logic here, leaving the back end for the next chapter. In fact, we just do the interface request and replace the iframe address parameter, so that we can easily generate our online page. All of the site’s component pages are structured in this way, and the preview is also an iframe, so all of the components have the perfect time to trigger their preview.

NPM release

I was hesitant to write this section because the main body is basically the back end, but as one of the most important features of the site, what does the front end do

In the front end, the final interface is actually only two parameters, one is the release NPM package ID and the other is the release version number (the version number format is X.X.X).

Fatal problem: At this stage, the query of release package status and result has not been completed. This problem will be solved in the future. In addition, the current release packages are all in my own space. In fact, the back end already supports you to publish through your own NPM account, which will be improved later. The following is a picture of the release of packages in the current space

In the middle of the two packages are the previous article, about how I am lazy in git submission and standard (based on NodeJS) internal git automatic submission, Mr Request, now added to the automatic generation of release log, if you are interested in private chat with me.

Component input value verification

The logic of this section is complicated, and it is recommended to watch it slowly when you are in a good mood

This section follows the component structure-validation structure section above

First of all, we followed the above content to introduce the overall verification process, so how do I realize the whole process? Look at what I did backwards:

In the component

const { inputObject } = new InputTools(props, emit, input); // Object bindingCopy the code

Yes, that’s about it.

In the type common component

import { BindingObj } from '@/types/validation'; import ValidaDue, { RefDom, ValidaPorops } from '@/components/utils/ValidaDue'; export default class InputTools extends ValidaDue { inputObject: BindingObj /** * constructor * @param {ValidaPorops} props passes pass object * @param {Function} emit callback * @param {Element} dom passes listener DOM */ constructor(props: ValidaPorops, emit: Function, dom: RefDom) { super(props, emit, dom); this.inputObject = this.ValidaObject; }}Copy the code

Is also a simple constructor is completed, using the idea of inheritance, basically the content is through inheritance, the benefits of doing this need not say more, easy to expand at the same time also reduce the amount of code, is a code coupling and decoupling better way.

What about the next level?

In public check

export default class ValidaDue { value: any ValidaObject: BindingObj @ValidateDec.validationFn private static validation: Function @ValidateDec.resetStatus private static resetStatus: Function @ValidateDec.registerTrigger private static registerTrigger: Function /** * constructor * @param {ValidaPorops} props passes pass object * @param {Function} emit callback Function * @param {Element} dom passes listener DOM */  constructor(props: ValidaPorops, emit: Function, dom: RefDom) { this.value = ref(props.modelValue); emit('update:modelValue', this.value); this.ValidaObject = reactive({ value: this.value, rules: props.rules, check: true, errorMsg: '' }); this.ValidaObject.validation = ValidaDue.validation(this.ValidaObject); this.ValidaObject.resetStatus = ValidaDue.resetStatus(this.ValidaObject); onMounted(() => { if (this.ValidaObject.validation) ValidaDue.registerTrigger(dom.value, this.ValidaObject); }); const obj: any = inject('form', null); obj? .push(this.ValidaObject); }}Copy the code

It’s a little bit too much code, but it’s just a little bit. What we’ve done here is we’ve constructed a ValidaObject to return, and this is our return object, which contains the binding check information and the result and the event for this value. At the same time, we also use inject to transfer the upper and lower components. If you are familiar with element source code, you may guess that in form, it is the same as receiving provide in form.

Check decorator

This is the end of this chain, because the amount of code is a little bit more, I will separate the method to comb out the explanation, first on the flow chart:The main method is obviously the main build method, which registers the event by building an array. So let’s analyze each function backwards

ResetStatus (reset method)
/** * Resets parameters to default status * @param {*} target des Changes the source * @param {String} propertyKey Key value * @returns {void} */ static resetStatus(target: any, propertyKey: string | symbol): void { target[propertyKey] = (that: BindingObj): Function => { const setValue = that.value; function resetThat() { that.value = setValue; that.check = true; that.validation && that.validation.checkList.forEach((item: Validated) => { item.check = true; }); } return resetThat; }; }Copy the code

The reset method is an externally exposed method that can be called directly from the object

Since we are using a decorator build, we write this pattern, which executes as intermediate resetThat, returns the value by saving the value of the initial state and resets the state to the checksum state true (including the subordinate rules).

RegisterTrigger (registration event)
/** * Registers listening events * @param {*} target des Changes the source * @param {String} propertyKey Key value * @returns {void} */ static registerTrigger(target: any, propertyKey: string | symbol): void { target[propertyKey] = (dom: Element, that: BindingObj): void => { const getAllTrigger: string[] = []; that.validation.checkList.forEach((item: Validated) => { if (item.trigger && item.trigger.length > 0) { item.trigger.forEach((name: string) => { if (getAllTrigger.indexOf(name) === -1) getAllTrigger.push(name); }); }}); that.rules?.trigger?.forEach((name: string) => { if (getAllTrigger.indexOf(name) === -1) getAllTrigger.push(name); }); getAllTrigger.forEach((name: string) => { dom.addEventListener(name, () => { that.validation(name); }); }); }; }Copy the code

This is where we need to start filling in the Settings aspect. You can see that above the code module I have done two aspects

  • The first is that the events in the checkList are registered one by one
  • The second is to register the object containing trigger in the Rules array

This is where you need to look at the d.ts file for the entire Rules object

Export interface BindingObj {value: any; rules? : ValidaRule; check: boolean; errorMsg: string; validation? : any; resetStatus? : Function | any; } export interface ValidaRule { validate? : (string | ValidateS)[]; message? : string; trigger? : string[]; } // export interface ValidateS {validateName: string; message? : string; trigger? : string[]; } export interface Validated { validateName: string; check: boolean; message? : string; trigger: string[]; backMessage? : string; }Copy the code

These are real warriors, not to mention my head hurts, but the list of rules above is simply such an object

{validate: [{validateName: 'Required ', // validate rule trigger: ['input'] // trigger event}, 'HtmlTag' // validate rule], trigger: ['blur'] // Public trigger events}Copy the code

So you can see why there are two registration events up here, to satisfy a common trigger and a single trigger, right

Take a sip of water and calm down. What the hell is this

RunValidFn (perform validation method)
* @param {Validated} Validated Object * @param {Object} that Passed Object * @returns {{check: Boolean}} Returns the processed object */ private static async runValidFn(validated: validated, that: BindingObj): Promise<boolean> { let check; try { check = await (ValidateRule as any)[validated.validateName](that.value); } catch (e) { throw Error.ValidateRule; } try { validated.backMessage = validated.message ? validated.message : (DefaultMsg as any)[validated.validateName][check]; } catch (e) { throw Error.DefaultMsg; } return check; }Copy the code

The reason to go directly to the code is because this method is private and cannot be called externally. Here’s what this method does:

  • Emmm, the first step performs regular verification
  • The second part performs the checksum prompt text assignment
  • The third step returns the verification result

Wow is the easiest way to do it? Keep going

DealValidates (Perform array processing)
/** * Perform Array processing * @param {Array} validates rule * @param {Array} trigger Basic trigger event * @RETURNS {Validated[]} Returns an Array */ private static dealValidates(validates: (string | ValidateS)[], trigger: string[]) { const FnList: Validated[] = []; validates.forEach((item: string | ValidateS) => { if (typeof item === 'string') { FnList.push({ validateName: item, check: true, trigger: trigger }); } else { FnList.push({ validateName: item.validateName, check: true, trigger: item.trigger ? item.trigger : trigger, message: item.message }); }}); return FnList; }Copy the code

It is also very simple as a private method, which loops through the validation rules and generates the array of objects we need.

ValidationFn (main build method)

Finally, we can look at our main build method:

@param {Object} target Performs class * @param {String} propertyKey assignment key * @returns {void} */ static validationFn(target: any, propertyKey: string | symbol): void { target[propertyKey] = (that: any): Function | undefined => { if (! that.rules) return; const checkList = ValidateDec.dealValidates(that.rules.validate, that.rules.trigger); const backFn = async function(name: string, isCheckAll: boolean) { let check = true; let otherMessage = ''; for (let i = 0; i < checkList.length; i++) { const item: Validated = checkList[i]; if (item.trigger.indexOf(name) === -1 && item.check && ! isCheckAll) continue; item.check = await ValidateDec.runValidFn(item, that); if (! item.check) check = false; if (! item.check && item.backMessage) otherMessage = otherMessage ? ${otherMessage}, ${item.backMessage} ': item.backMessage; } that. ErrorMsg = '${that.rules.message? That.rules.message: ''} ${otherMessage?' : '+ otherMessage: ''}'; that.check = check; }; backFn.checkList = checkList; return backFn; }; }Copy the code
  • First of all this is a constructor, so there is a basic template to override
  • The first step is to build rules into the array of objects we need
  • Then inside the method, we loop through the array we just generated
  • Each rule that needs to be executed returns the structure
  • Finally, the return status and return information are generated

When we comb each method step by step, the final method is not difficult, it is a process from shallow to deep.

Form form

Do you think it’s over? The form validation we mentioned just now hasn’t happened yet. Unlike Element, validation here applies only to a set of functions, not to style constraints.

Since there are plenty of examples of this in other frameworks, let’s take a quick look at setup:

setup() { /** *************************************************************************************************/ /** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * register dojo.provide * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * *************************************************************************************************/ const form = reactive([]); provide('form', form); / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * callback methods * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / / * * *************************************************************************************************/ const TypeFormApi = Reactive ({// resets the form resetForm() {nextTick() => {form.foreach ((item) => {item.resetStatus &&item.resetStatus (); }); }); }, // check before submitting the form async validate() {const backList = []; for (let i = 0; i < form.length; i++) { form[i].validation && await form[i].validation('', true); if (! form[i].check) backList.push(form[i]); } return backList; }}); return { form, TypeFormApi }; }Copy the code

It is to use provide to obtain parameters, and then verify each parameter one by one. The final result is realized by returning the verification array. The logic is not too complicated.

For you the significance of the project

It has been nearly half a year since the beginning of the project in April to now in September, and the future development and results of the project are completely unforeseeable for individual developers.


However, I think this is actually one of the most complete and large projects I have seen using VUe3 recently (the official recommendation is not to use it in official projects for the time being), so I think this project can at least be used as a template for future development using VUe3. There are a lot of components inside, and creating a new component doesn’t require much logic, just style changes.


Secondly, as a beginner, it is also a good way to improve. I think it is a very good way to learn about the detailed configuration of each content in the project and to think about why it is configured in this way (I started learning VUE by configuring myself and then watching others configure it).

And, of course, if want to use this program I have personal or company is very welcome, I can also help people to make certain changes, this thing as an internal communication platform are still a little significance (platform, community-based personal power can’t support), so it is welcome to contact me.

Then, as a VUE3 stomp pit project, the internal configuration has also been set, and we can also use it directly. It is also possible to use this project as a revision of our own project. The existing configuration mainly includes:

  • API Interface design
  • Axios encapsulation
  • Several commonly used directives
  • Component validation for numeric binding (validation rule)
  • Rapid creation and switching of the thematic style

Project summary

In addition to the appeal of a design model and rules in fact, when writing there are other problems and small pits, in this if one by one to explain the change is too tedious, so interested students can directly download the source code to view.

In this also for some use of the question to answer, when we release the version of the NPM package format should be X.X.X number, so unfamiliar with the NPM package students remember to pay attention to oh, we can rest assured that the RELEASE and operation of the NPM package, here will be automated backup of the data every day. You can also try installing the NPM package to see if it really works, by looking at how each component’s index.vue file works.

Bug moment

This is basically the end of the article, so this chapter let’s take a look at the vue bugs and my experience to relax

Invalid proxy

I do not know whether it is my own reason, when I set the proxy proxy in vue3 project vue.config.js, there is no effect!

'proxy' : {'/' : {target: 'http://36.111.183.168:9527', changeOrigin: true}}Copy the code

If you know what’s going on, please leave a comment

The router is not intelligent

We don’t need to know what the directory structure of the final site will look like after we package the VUe2 project, for example: http://36.111.183.168:9527/assemble/#/Show/index in the address, I do not need to know this page is accessed the assemble path, but not in vue3, Normally don’t do processing change becomes so file request into http://36.111.183.168:9527/ *. Such as CSS, not sure I can’t access what should I do?

const router: Router = createRouter({
    history: createWebHashHistory(window.location.pathname),
    routes
});
Copy the code

It is not smart to specify the access route, a small statement can also achieve this function,

Then I made an issue: github.com/vuejs/vue-r… And the answer is:

Please open the issue in English and with a boiled down reproduction.

This is… Ok I change big brother, new issue to: github.com/vuejs/vue-r… (Baidu translation is easy to use)

And then after two rounds of repeating the question,

Next time provide a repro using the starter at new-issue.vuejs.org/?repo=vuejs… . I had a very hard time understanding the bug you were reporting

Next time use the starter version on * to provide replication. I find it difficult to understand the error you reported.

I… Know the big brother raised questions github.com/vuejs/vue-r… It is said that it has been fixed, but I updated the vue-router-next package before and still have this problem:), so you can pay attention to this thing when using it. (My Baidu translation is so unreliable?)

The next chapter forecast

Next chapter: A not trivial project practice for Vue3.0 (3) (the title will be changed to a link when the article is completed)

Main content: How I used springMVC pattern to write a Node back end (not necessarily good, but I think it’s a way to get closer to object-oriented programming)

Preview code:

@routerDec.BaseRequest('/template/templateControl') export class TemplateControl { private static TemplateService: TemplateService = new TemplateService ()/template data obtained through id * * * * @ the route POST/template/templateControl/getTemplateById * @group Third-party package management * @param {string} ids.formData Search name * @RETURNS {Promise} 200 - Returns query result * @RETURNS {Promise} 500 - Returns error cause */  @routerDec.RequestMapping('/getTemplateById', MyType.post) async getTemplateById( @routerDec.RequestParams('String', 'ids') ids: string, @routerDec.Response() res: express.Response ): Promise<void> { const response = new BaseResponse<Template[]>(); try { if (ids) { response._datas = await TemplateControl.TemplateService.getTemplateByIds(ids); } response.changeType(BackType.success); } catch (e) { response._msg = BaseErrorMsg.sqlError; } res.json(response); } / query template name * * * * @ the route POST/template/templateControl/getTemplateName * * @ @ group, the third party package management param {string} ids. The formData Search name * @returns {Promise} 200 - Returns query result * @returns {Promise} 500 - Returns error cause */ @routerDec.RequestMapping('/getTemplateName', MyType.post) async getTemplateName( @routerDec.Response() res: express.Response ): Promise<void> { const response = new BaseResponse<TemplateName[]>(); try { response._datas = await TemplateControl.TemplateService.getTemplateName(); response.changeType(BackType.success); } catch (e) { response._msg = BaseErrorMsg.sqlError; } res.json(response); }}Copy the code

emmm.. Thick Java controller style, taste too strong

The source code

The following operations, including WebHooks, are done on the Gitee cloud because it is difficult to access Github. Welcome to download the source code to a little star oh

If you want to run it, you can switch to the RUN branch and have direct access to the interface environment on the line

Source code address: gitee.com/beon/vue-as…

Run the NPM run serve command

At the end

This is all the content of this article, if you have any questions or want me to help with the development of private chat, welcome to comment on me, the following affixed my wechat TWO-DIMENSIONAL code.