├── .gitignore ├── README.md ├── babel.config.js ├── docs ├── .gitignore ├── package.json ├── src │ ├── .vuepress │ │ ├── config.js │ │ ├── enhanceApp.js │ │ ├── public │ │ │ └── frontend-masters-logo.png │ │ └── styles │ │ │ ├── index.styl │ │ │ └── palette.styl │ ├── guide │ │ ├── components.md │ │ ├── core-principles.md │ │ ├── easy-to-follow-best-practices.md │ │ ├── final-thoughts.md │ │ ├── index.md │ │ ├── languages.md │ │ ├── reusability-and-composition.md │ │ ├── routing.md │ │ ├── state-management.md │ │ ├── testing.md │ │ └── vue-cli.md │ └── index.md └── yarn.lock ├── package.json ├── public ├── favicon.ico └── index.html ├── resources └── Production-Grade-Vue.pdf ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ ├── CompositionCounter.vue │ ├── DynamicHeading.vue │ ├── HelloWorld.vue │ └── ManagingTernaryStyles.vue └── main.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | 25 | # Local Netlify folder 26 | .netlify -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Production-Grade Vue 2 | 3 | The official repo for [Frontend Masters Workshop: Production-Grade Vue](https://frontendmasters.com/courses/production-vue/)! 4 | 5 | ## Resources 6 | 7 | - [Slides](https://github.com/bencodezen/production-grade-vue/blob/01-challenge-solution/resources/Production-Grade-Vue.pdf) 8 | - [Official Docs](https://production-grade-vue.bencodezen.io/) 9 | 10 | ## Challenges 11 | 12 | - 01: Render Function + CSS Modules 13 | - [Challenge Description](https://production-grade-vue.bencodezen.io/guide/languages.html#challenge-01) 14 | - [Solution Branch](https://github.com/bencodezen/production-grade-vue/tree/01-challenge-solution) 15 | - 02: Composition API Counter 16 | - [Challenge Description](https://production-grade-vue.bencodezen.io/guide/reusability-and-composition.html#challenge-02) 17 | - [Solution Branch](https://github.com/bencodezen/production-grade-vue/tree/02-challenge-counter-solution) 18 | 19 | ## Setting Up Local Environment 20 | 21 | ```bash 22 | # Install dependencies 23 | yarn install 24 | 25 | # Start local dev server 26 | yarn serve 27 | ``` 28 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | pids 2 | logs 3 | node_modules 4 | npm-debug.log 5 | coverage/ 6 | run 7 | dist 8 | .DS_Store 9 | .nyc_output 10 | .basement 11 | config.local.js 12 | basement_dist 13 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "production-grade-vue-docs", 3 | "version": "0.0.1", 4 | "description": "Documentation for Production-Grade Vue with Frontend Masters", 5 | "main": "index.js", 6 | "authors": { 7 | "name": "Ben Hong", 8 | "email": "ben@bencodezen.io" 9 | }, 10 | "repository": "https://github.com/bencodezen/production-grade-vue/production-grade-vue-docs", 11 | "scripts": { 12 | "dev": "vuepress dev src", 13 | "build": "vuepress build src" 14 | }, 15 | "license": "MIT", 16 | "devDependencies": { 17 | "vuepress": "^1.7.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/src/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const { description } = require('../../package'); 2 | 3 | module.exports = { 4 | /** 5 | * Ref:https://v1.vuepress.vuejs.org/config/#title 6 | */ 7 | title: 'Production-Grade Vue', 8 | /** 9 | * Ref:https://v1.vuepress.vuejs.org/config/#description 10 | */ 11 | description: 12 | 'Building applications that can scale and grow is more than simply following a series of rules. In this workshop, you will learn proven patterns for building production Vue.js apps while gaining an understanding of why certain patterns exist. Equipped with this, you will feel more confident in choosing what is best for your application.', 13 | 14 | /** 15 | * Extra tags to be injected to the page HTML `
` 16 | * 17 | * ref:https://v1.vuepress.vuejs.org/config/#head 18 | */ 19 | head: [ 20 | ['meta', { name: 'theme-color', content: '#3eaf7c' }], 21 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], 22 | [ 23 | 'meta', 24 | { name: 'apple-mobile-web-app-status-bar-style', content: 'black' } 25 | ] 26 | ], 27 | 28 | /** 29 | * Theme configuration, here is the default theme configuration for VuePress. 30 | * 31 | * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html 32 | */ 33 | themeConfig: { 34 | repo: 'https://github.com/bencodezen/production-grade-vue', 35 | editLinks: false, 36 | docsDir: 'docs', 37 | editLinkText: 'Edit this page', 38 | lastUpdated: true, 39 | nav: [ 40 | { 41 | text: 'Guide', 42 | link: '/guide/' 43 | }, 44 | { 45 | text: 'Config', 46 | link: '/config/' 47 | }, 48 | { 49 | text: 'VuePress', 50 | link: 'https://v1.vuepress.vuejs.org' 51 | } 52 | ], 53 | sidebar: [ 54 | '/guide/', 55 | '/guide/languages', 56 | '/guide/vue-cli', 57 | '/guide/components', 58 | '/guide/reusability-and-composition', 59 | '/guide/state-management', 60 | '/guide/routing', 61 | '/guide/testing', 62 | '/guide/easy-to-follow-best-practices', 63 | '/guide/core-principles', 64 | '/guide/final-thoughts' 65 | ] 66 | }, 67 | 68 | /** 69 | * Apply plugins,ref:https://v1.vuepress.vuejs.org/zh/plugin/ 70 | */ 71 | plugins: ['@vuepress/plugin-back-to-top', '@vuepress/plugin-medium-zoom'] 72 | }; 73 | -------------------------------------------------------------------------------- /docs/src/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Client app enhancement file. 3 | * 4 | * https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements 5 | */ 6 | 7 | export default ({ 8 | Vue, // the version of Vue being used in the VuePress app 9 | options, // the options for the root Vue instance 10 | router, // the router instance for the app 11 | siteData // site metadata 12 | }) => { 13 | // ...apply enhancements for the site. 14 | } 15 | -------------------------------------------------------------------------------- /docs/src/.vuepress/public/frontend-masters-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencodezen/production-grade-vue/7b298c41dbc8f0266482b50fb66721fed7fff4aa/docs/src/.vuepress/public/frontend-masters-logo.png -------------------------------------------------------------------------------- /docs/src/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom Styles here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/config/#index-styl 5 | */ 6 | 7 | .home .hero img 8 | max-width 450px!important 9 | -------------------------------------------------------------------------------- /docs/src/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom palette here. 3 | * 4 | * ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl 5 | */ 6 | 7 | $accentColor = #3eaf7c 8 | $textColor = #2c3e50 9 | $borderColor = #eaecef 10 | $codeBgColor = #282c34 11 | -------------------------------------------------------------------------------- /docs/src/guide/components.md: -------------------------------------------------------------------------------- 1 | # Components 2 | 3 | ## Part 1 4 | 5 | ### Naming Components 6 | 7 | - Avoid single word components 8 | - Prefix components with words like `App` or `Base` or a unique company/team identifier 9 | - For component where only a single instance should exist, prefix with `The` 10 | - Tightly related components should be also named as such to maintain an obvious relationship between them: `TodoList > TodoListItem > TodoListItemName` 11 | 12 | ### Naming Component Methods 13 | 14 | - Use descriptive names (e.g., `updateUserName`) 15 | - Do not name it after the event (e.g., `onInput`) 16 | - When you name it after the event, this assumes where it will be called and will make refactoring difficult later on 17 | - Prefer destructuring over multiple argument. In other words, allow users to "configure" their options by letting them assign values to an object rather than memorizing what order the parameters are in 18 | 19 | ```js 20 | // * Recommended 21 | updateUser ({ userList, index, value, isOnline }) { 22 | if (isOnline) { 23 | userList[index] = value 24 | } else { 25 | this.removeUser(userList, index) 26 | } 27 | } 28 | 29 | // ! Not recommended 30 | updateUser (userList, index, value, isOnline) { 31 | if (isOnline) { 32 | userList[index] = value 33 | } else { 34 | this.removeUser(userList, index) 35 | } 36 | } 37 | ``` 38 | 39 | ### When to Refactor Components 40 | 41 | - Data Driven Refactoring 42 | - Signs you need more components 43 | - When your components are hard to understand 44 | - You feel a fragment of a component could use its own state 45 | - Hard to describe what what the component is actually responsible for 46 | - How to find reusable components 47 | - Look for v-for loops 48 | - Look for large components 49 | - Look for similar visual designs 50 | - Look for repeating interface fragments 51 | - Look for multiple/mixed responsibilities 52 | - Look for complicated data paths 53 | 54 | ### SFC Block Order 55 | 56 | Recommend using `script > template > style` in order to optimize for scrolling experience for better shared concerns amongst the file. 57 | 58 | ```vue 59 | 64 | 65 | 66 |This should be red!
135 | 136 | 137 | 145 | ``` 146 | 147 | ### CSS Modules 148 | 149 | What I recommend in terms of managing styles inside of components. It generates a unique CSS class that avoid the specificity issue. 150 | 151 | ```vue 152 | 153 |This should be red!
154 | 155 | 156 | 164 | ``` 165 | 166 | ## Challenge 01 167 | 168 | ### In the repo 169 | 170 | - Rewrite the `DynamicHeading` component using the render function 171 | - Refactor the `#app` styles to a CSS class and use CSS modules 172 | 173 | ### In your app 174 | 175 | - Look around to see if there are any components that might benefit from using the render method instead of templates 176 | - Refactor a component to use CSS modules 177 | -------------------------------------------------------------------------------- /docs/src/guide/reusability-and-composition.md: -------------------------------------------------------------------------------- 1 | # Reusability and Composition 2 | 3 | ## Mixins 4 | 5 | - Pros 6 | - Relatively easy to use 7 | - Good for refactoring 8 | - Cons 9 | - Possible properties name clashes. 10 | - Can’t share template fragments 11 | - Gets harder to track where things are coming from once there are more mixins 12 | 13 | ## Provide / Inject 14 | 15 | - Pros 16 | - Easy sharing data and methods with descendants 17 | - Helps avoiding unnecessary props 18 | - Components can choose which properties to inject 19 | - Can be used to provide default props and data values 20 | - Cons 21 | - There are some reactivity caveats when it comes to usage in Vue 2 22 | - Creates a tight relationship between two components that is not immediately apparent 23 | - There is ambiguity when it comes to what is coming from where 24 | 25 | ## Composition API 26 | 27 | - Resource: [Official Vue.js 3 Composition API Guide](https://v3.vuejs.org/guide/composition-api-introduction.html) 28 | 29 | ## Challenge 02 30 | 31 | - Create a counter that uses the Composition API to: 32 | - Keep track of a currentCount 33 | - Increment the count 34 | - Change the amount that the count can be incremented by 35 | -------------------------------------------------------------------------------- /docs/src/guide/routing.md: -------------------------------------------------------------------------------- 1 | ## Routing 2 | 3 | - Three main categories of routing 4 | - View Components - Definition for page level components (i.e., Home, About, Dashboard) 5 | - Layout Components - Markup shared between pages (i.e., header, sidebar, etc.) 6 | - Routes - Define how paths map to view components 7 | - Add dynamic key to route-view components 8 | - vue-meta 9 | - Lazy load routes 10 | -------------------------------------------------------------------------------- /docs/src/guide/state-management.md: -------------------------------------------------------------------------------- 1 | # State Management 2 | 3 | - 📢 Need an introduction to Vuex? Check out Sarah's course: Intro to Vue 3! 4 | - 📢 Need a deeper dive Vuex? Check out Divya's course: Vuex for Intermediate Vue.js Developers! 5 | 6 | ## What data to put into Vuex? 7 | 8 | - Data shared between components that might not be in direct parent-child relation 9 | - Data that you want to keep between router views (for example lists of records fetched from the API) 10 | - Route params are more important though (as a source of truth) 11 | - Any kind of global state 12 | - Examples: login status, user information, global notifications 13 | - Anything if you feel it will make managing it simpler 14 | 15 | ## What data to avoid putting into Vuex? 16 | 17 | - User Interface variables 18 | - Examples: isDropdownOpen, isInputFocused, isModalVisible 19 | - Forms data 20 | - Validation results 21 | - Single records from the API 22 | 23 | ## Other advice 24 | 25 | - Avoid calling mutations directly in components 26 | - Use built-in map helpers 27 | - mapActions vs mapMutations 28 | - 📢 Need an intro to mapHelpers to Vuex? Check out Divya's course: Vuex for Intermediate Vue.js Developers! 29 | - Don't use mapMutations 30 | - Different ways to define mapActions 31 | - Always use namespaced module 32 | - 📢 Need an intro to modules to Vuex? Check out Divya's course: Vuex for Intermediate Vue.js Developers! 33 | - Mutations and actions are not namespsaced by default, this is why it's important to turn this on by default. 34 | - Composition API's Impact on Vuex 35 | -------------------------------------------------------------------------------- /docs/src/guide/testing.md: -------------------------------------------------------------------------------- 1 | ## Testing 2 | 3 | - The Pareto Principle 4 | - Best practice of writing unit tests 5 | - Don't test that Vue works 6 | - Examples: Checking that a data, computed, etc property exists 7 | - Primarily stick with shallow rendering 8 | - Otherwise a problem in a common component can break many tests 9 | - Build unit tests into generators 10 | - Traditional Testing Model 11 | - Unit tests are great, but they're often not the 20% we should be focusing on 12 | - The Two Tests to Focus On 13 | - Can the user login? 14 | - Can the user pay us? 15 | - Best practice for writing e2e tests 16 | - Don't maintain state between tests 17 | - Tests should be able to run independently of one another 18 | - Don't select elements with classes 19 | - Think from the user's perspective, or select elements by their intent 20 | -------------------------------------------------------------------------------- /docs/src/guide/vue-cli.md: -------------------------------------------------------------------------------- 1 | ## Vue CLI 2 | 3 | ### Vue CLI 4 | 5 | - Setup a Vue 3 project on the repo 6 | 7 | ### Vue UI GUI 8 | 9 | - Once Vue CLI is installed locally, you can run this with `vue ui` 10 | - Equivalent of Vue CLI with DX magic 11 | - Need to kill a port because something was left running? 12 | - Check out how easy it is to configure things 13 | - When you run your server, you also get statistics and analysis 14 | 15 | ### Vue CLI - Modern Mode 16 | 17 | - Babel allows us to utilize all the newest language features in ES6+, but this also means that traditional bundling strategies meant you had to include all of this to users who didn't need it 18 | - With modern mode, Vue CLI products two versions of your app: one modern bundle targeting browsers that support ES modules, and a legacy bundle targeting older browsers that do not. 19 | - The best thing about this is that beyond ensuring your build process has this flag checked off, you don't need to do anything else. 20 | - The command is `vue-cli-service build --modern` 21 | 22 | ### Valid alternatives to Vue CLI 23 | 24 | - Micro-frontends 25 | - Legacy migration 26 | - Server-side rendering 27 | -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: https://v1.vuepress.vuejs.org/hero.png 4 | tagline: Official guide for Production-Grade Vue with Frontend Masters 5 | actionText: Read the Guide → 6 | actionLink: /guide/ 7 | footer: Made by Ben Hong with ❤️ 8 | --- 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "production-grade-vue", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "vue": "^3.0.0" 13 | }, 14 | "devDependencies": { 15 | "@vue/cli-plugin-babel": "~4.5.0", 16 | "@vue/cli-plugin-eslint": "~4.5.0", 17 | "@vue/cli-service": "~4.5.0", 18 | "@vue/compiler-sfc": "^3.0.0", 19 | "babel-eslint": "^10.1.0", 20 | "eslint": "^6.7.2", 21 | "eslint-plugin-vue": "^7.0.0-0" 22 | }, 23 | "eslintConfig": { 24 | "root": true, 25 | "env": { 26 | "node": true 27 | }, 28 | "extends": [ 29 | "plugin:vue/vue3-essential", 30 | "eslint:recommended" 31 | ], 32 | "parserOptions": { 33 | "parser": "babel-eslint" 34 | }, 35 | "rules": {} 36 | }, 37 | "browserslist": [ 38 | "> 1%", 39 | "last 2 versions", 40 | "not dead" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bencodezen/production-grade-vue/7b298c41dbc8f0266482b50fb66721fed7fff4aa/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |{{ currentCount }}
27 | 28 | 29 |
5 | For a guide and recipes on how to configure / customize this project,
6 | check out the
7 | vue-cli documentation.
8 |