├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .markdownlintrc ├── .prettierignore ├── .prettierrc.js ├── .vscode ├── extensions.json └── settings.json ├── .vuepress ├── components │ ├── vue-example-simple.vue │ └── vue-example-simple │ │ ├── code-block.vue │ │ ├── code-label.vue │ │ ├── example-result.vue │ │ └── playground-button.vue ├── config.js ├── examples │ ├── intro-attribute-binding.vue │ ├── intro-component-with-prop-complete.vue │ ├── intro-component-with-prop.vue │ ├── intro-component.vue │ ├── intro-components-nested.vue │ ├── intro-conditional.vue │ ├── intro-hello-world.vue │ ├── intro-loop.vue │ ├── intro-method.vue │ ├── intro-v-model.vue │ ├── script-dev.vue │ └── script-prod.vue ├── store.js └── utils │ ├── build-example-attrs.js │ ├── build-example-component.js │ ├── modern-to-es5.js │ ├── parse-example-args.js │ ├── parse-example.js │ ├── prettify-js.js │ ├── sanitize-attr-value.js │ └── use-markdown-it-vue-example.js ├── README.md ├── aliases.config.js ├── demos └── intro.md ├── lint-staged.config.js ├── package.json ├── stylelint.config.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | sourceType: 'script', 5 | }, 6 | extends: [ 7 | 'plugin:vue-libs/recommended', 8 | // https://github.com/vuejs/eslint-plugin-vue#bulb-rules 9 | 'plugin:vue/recommended', 10 | // https://github.com/prettier/eslint-config-prettier 11 | 'prettier', 12 | 'prettier/standard', 13 | ], 14 | plugins: ['node'], 15 | rules: { 16 | // Only allow debugger in development 17 | 'no-debugger': process.env.PRE_COMMIT ? 'error' : 'off', 18 | // Only allow `console.log` in development 19 | 'no-console': process.env.PRE_COMMIT 20 | ? ['error', { allow: ['warn', 'error'] }] 21 | : 'off', 22 | }, 23 | overrides: [ 24 | { 25 | files: ['.vuepress/components/**/*', '.vuepress/store.js'], 26 | parserOptions: { 27 | parser: 'babel-eslint', 28 | sourceType: 'module', 29 | }, 30 | env: { 31 | browser: true, 32 | }, 33 | }, 34 | ], 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS Files 2 | .DS_Store 3 | Thumbs.db 4 | 5 | # Dependencies 6 | node_modules/ 7 | 8 | # Dev/Build Artifacts 9 | dist/ 10 | 11 | # Local Env Files 12 | .env.local 13 | .env.*.local 14 | 15 | # Log Files 16 | *.log 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | # Unconfigured Editors 22 | .idea 23 | *.suo 24 | *.ntvs* 25 | *.njsproj 26 | *.sln 27 | -------------------------------------------------------------------------------- /.markdownlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | // Rule customizations for markdownlint go here 4 | // https://github.com/DavidAnson/markdownlint/blob/master/doc/Rules.md 5 | 6 | // Disable line length restrictions, because editor soft-wrapping is being 7 | // used instead. 8 | "line-length": false, 9 | "no-trailing-punctuation": false, 10 | "no-inline-html": false, 11 | // === 12 | // Prettier overrides 13 | // === 14 | "no-multiple-blanks": false, 15 | "list-marker-space": false 16 | } 17 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | dist/** 3 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 80, 3 | tabWidth: 2, 4 | useTabs: false, 5 | semi: false, 6 | singleQuote: true, 7 | trailingComma: 'es5', 8 | bracketSpacing: true, 9 | jsxBracketSameLine: false, 10 | arrowParens: 'avoid', 11 | proseWrap: 'never', 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | // Syntax highlighting and more for .vue files 6 | // https://github.com/vuejs/vetur 7 | "octref.vetur", 8 | // Lint-on-save with ESLint 9 | // https://github.com/Microsoft/vscode-eslint 10 | "dbaeumer.vscode-eslint", 11 | // Lint-on-save with Stylelint 12 | // https://github.com/shinnn/vscode-stylelint 13 | "shinnn.stylelint", 14 | // Format-on-save with Prettier 15 | "esbenp.prettier-vscode", 16 | // SCSS intellisense 17 | "mrmlnc.vscode-scss", 18 | // Lint markdown in README files 19 | "DavidAnson.vscode-markdownlint" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // === 3 | // Spacing 4 | // === 5 | "editor.insertSpaces": true, 6 | "editor.tabSize": 2, 7 | "editor.trimAutoWhitespace": true, 8 | "files.trimTrailingWhitespace": true, 9 | "files.eol": "\n", 10 | "files.insertFinalNewline": true, 11 | "files.trimFinalNewlines": true, 12 | // === 13 | // Files 14 | // === 15 | "files.exclude": { 16 | "**/*.log": true, 17 | "**/*.log*": true, 18 | "**/dist": true, 19 | "**/coverage": true 20 | }, 21 | "files.associations": { 22 | ".markdownlintrc": "jsonc" 23 | }, 24 | // === 25 | // Event Triggers 26 | // === 27 | "editor.formatOnSave": true, 28 | "eslint.autoFixOnSave": true, 29 | "eslint.run": "onSave", 30 | "eslint.validate": [ 31 | { "language": "javascript", "autoFix": true }, 32 | { "language": "javascriptreact", "autoFix": true }, 33 | { "language": "vue", "autoFix": true } 34 | ], 35 | // === 36 | // HTML 37 | // === 38 | "html.format.enable": false, 39 | "html.suggest.angular1": false, 40 | "html.suggest.ionic": false, 41 | "vetur.validation.template": false, 42 | // === 43 | // JS(ON) 44 | // === 45 | "javascript.format.enable": false, 46 | "json.format.enable": false, 47 | "vetur.validation.script": false, 48 | // === 49 | // CSS 50 | // === 51 | "stylelint.enable": true, 52 | "css.validate": false, 53 | "scss.validate": false, 54 | "vetur.validation.style": false, 55 | // === 56 | // MARKDOWN 57 | // === 58 | "[markdown]": { 59 | "editor.wordWrap": "wordWrapColumn", 60 | "editor.wordWrapColumn": 80 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.vuepress/components/vue-example-simple.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 155 | -------------------------------------------------------------------------------- /.vuepress/components/vue-example-simple/code-block.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 65 | 66 | 103 | -------------------------------------------------------------------------------- /.vuepress/components/vue-example-simple/code-label.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 44 | 45 | 63 | -------------------------------------------------------------------------------- /.vuepress/components/vue-example-simple/example-result.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 74 | 75 | 89 | -------------------------------------------------------------------------------- /.vuepress/components/vue-example-simple/playground-button.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 72 | 73 | 78 | -------------------------------------------------------------------------------- /.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const useMarkdownItVueExample = require('./utils/use-markdown-it-vue-example') 2 | 3 | module.exports = { 4 | title: 'VuePress Enhanced Examples', 5 | configureWebpack: { 6 | resolve: { 7 | alias: require('../aliases.config').webpack, 8 | }, 9 | }, 10 | markdown: { 11 | config: useMarkdownItVueExample, 12 | }, 13 | themeConfig: { 14 | sidebar: [['/', 'Guide'], ['/demos/intro', 'DEMO: Introduction']], 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-attribute-binding.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-component-with-prop-complete.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 37 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-component-with-prop.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-component.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-components-nested.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-conditional.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-hello-world.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-loop.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 23 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-method.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 24 | -------------------------------------------------------------------------------- /.vuepress/examples/intro-v-model.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | -------------------------------------------------------------------------------- /.vuepress/examples/script-dev.vue: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /.vuepress/examples/script-prod.vue: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /.vuepress/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | const globalSettingsScope = '__vueExampleGlobalSettings' 4 | const data = { 5 | jsStyle: 'es5', 6 | useDirectiveShorthands: false, 7 | } 8 | 9 | if (typeof window !== 'undefined') { 10 | for (const dataPropertyName in data) { 11 | const defaultValue = data[dataPropertyName] 12 | const savedValue = window.localStorage.getItem( 13 | `${globalSettingsScope}.${dataPropertyName}` 14 | ) 15 | data[dataPropertyName] = savedValue ? JSON.parse(savedValue) : defaultValue 16 | } 17 | } 18 | 19 | export default new Vue({ 20 | data, 21 | watch: { 22 | ...Object.keys(data) 23 | .map(dataPropertyName => ({ 24 | [dataPropertyName](newValue) { 25 | if (typeof window !== 'undefined') { 26 | window.localStorage.setItem( 27 | `${globalSettingsScope}.${dataPropertyName}`, 28 | JSON.stringify(newValue) 29 | ) 30 | } 31 | }, 32 | })) 33 | .reduce((a, b) => ({ ...a, ...b }), {}), 34 | }, 35 | }) 36 | -------------------------------------------------------------------------------- /.vuepress/utils/build-example-attrs.js: -------------------------------------------------------------------------------- 1 | const flow = require('lodash/flow') 2 | const sanitizeAttrValue = require('./sanitize-attr-value') 3 | const modernToEs5 = require('./modern-to-es5') 4 | const prettifyJs = require('./prettify-js') 5 | 6 | module.exports = function buildExampleAttrs( 7 | fileBaseName, 8 | { html, js, css }, 9 | attrs 10 | ) { 11 | return ` 12 | name="${fileBaseName}" 13 | html="${sanitizeAttrValue(html)}" 14 | es5-js="${flow( 15 | modernToEs5, 16 | prettifyJs, 17 | sanitizeAttrValue 18 | )(js)}" 19 | modern-js="${flow( 20 | prettifyJs, 21 | sanitizeAttrValue 22 | )(js)}" 23 | css="${sanitizeAttrValue(css)}" 24 | ${attrs.replace(/,/g, ' ')} 25 | ` 26 | } 27 | -------------------------------------------------------------------------------- /.vuepress/utils/build-example-component.js: -------------------------------------------------------------------------------- 1 | const parseExampleArgs = require('./parse-example-args') 2 | const parseExample = require('./parse-example') 3 | const buildExampleAttrs = require('./build-example-attrs') 4 | 5 | module.exports = function buildExampleComponent(args) { 6 | const { fileBaseName, attrs } = parseExampleArgs(args) 7 | const example = parseExample(fileBaseName) 8 | const exampleArgs = buildExampleAttrs(fileBaseName, example, attrs) 9 | return `` 10 | } 11 | -------------------------------------------------------------------------------- /.vuepress/utils/modern-to-es5.js: -------------------------------------------------------------------------------- 1 | const { transform } = require('buble') 2 | 3 | module.exports = function modernToEs5(code) { 4 | return transform(code, { 5 | target: { ie: 9 }, 6 | transforms: { 7 | templateString: false, 8 | }, 9 | namedFunctionExpressions: false, 10 | }).code.replace(/\bthis\$1\b/g, 'vm') 11 | } 12 | -------------------------------------------------------------------------------- /.vuepress/utils/parse-example-args.js: -------------------------------------------------------------------------------- 1 | module.exports = function parseExampleArgs(args) { 2 | const fileBaseName = args.match(/^([^,]+)(\s*,)?/)[1].trim() 3 | const attrsMatch = args.match(/^[^,]+\s*,\s*(.+?)$/) 4 | const attrs = attrsMatch ? attrsMatch[1].trim() : '' 5 | 6 | return { fileBaseName, attrs } 7 | } 8 | -------------------------------------------------------------------------------- /.vuepress/utils/parse-example.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | const { parse } = require('@vue/component-compiler-utils') 4 | const VueTemplateCompiler = require('vue-template-compiler') 5 | 6 | module.exports = function parseExample(fileBaseName) { 7 | const filePath = path.resolve(__dirname, `../examples/${fileBaseName}.vue`) 8 | const componentSource = fs.readFileSync(filePath, { encoding: 'utf8' }) 9 | const { template, script, styles } = parse({ 10 | filename: filePath, 11 | source: componentSource, 12 | compiler: VueTemplateCompiler, 13 | needMap: false, 14 | }) 15 | return { 16 | html: template ? template.content : '', 17 | js: script ? script.content : '', 18 | css: styles.length ? styles[0].content : '', 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.vuepress/utils/prettify-js.js: -------------------------------------------------------------------------------- 1 | const prettier = require('prettier/standalone') 2 | const prettierParserBabylon = require('prettier/parser-babylon') 3 | 4 | module.exports = function prettifyJs(code) { 5 | return prettier.format(code, { 6 | parser: 'babylon', 7 | plugins: [prettierParserBabylon], 8 | printWidth: 80, 9 | tabWidth: 2, 10 | useTabs: false, 11 | semi: false, 12 | singleQuote: true, 13 | trailingComma: 'none', 14 | bracketSpacing: true, 15 | jsxBracketSameLine: false, 16 | arrowParens: 'avoid', 17 | proseWrap: 'never', 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /.vuepress/utils/sanitize-attr-value.js: -------------------------------------------------------------------------------- 1 | const stripIndent = require('strip-indent') 2 | 3 | module.exports = function sanitizeAttrValue(text) { 4 | return stripIndent(text) 5 | .replace(/"/g, '"') 6 | .replace(/\{\{/g, '[[') 7 | .replace(/\}\}/g, ']]') 8 | .replace(/\/\/\n/g, '') 9 | .trim() 10 | } 11 | -------------------------------------------------------------------------------- /.vuepress/utils/use-markdown-it-vue-example.js: -------------------------------------------------------------------------------- 1 | const customBlock = require('markdown-it-custom-block') 2 | const buildExampleComponent = require('./build-example-component') 3 | 4 | module.exports = function useMarkdownItVueExample(md) { 5 | md.use(customBlock, { 6 | example(args) { 7 | return buildExampleComponent(args) 8 | }, 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VuePress Enhanced Examples 2 | 3 | This is an attempt at providing a better experience for documentation authors and consumers, in regards to examples. 4 | 5 | ## Overview 6 | 7 | All examples are in `.vue` files, inside the `.vuepress/examples` directory. These examples can be referenced from documentation with: 8 | 9 | ```md 10 | @[example](name-of-example-file) 11 | ``` 12 | 13 | By default, this will render: 14 | 15 | - An HTML block (if a `template` is provided) 16 | - A JS block (if a `script` is provided) 17 | - A CSS block (if a `style` is provided) 18 | - A button to render the result in an iframe 19 | - A button to open the example in CodePen 20 | 21 | The example rendering is even configurable, e.g.: 22 | 23 | ```md 24 | 25 | 26 | @[example](name-of-example-file, js-only) 27 | ``` 28 | 29 | ```md 30 | 31 | 32 | @[example](name-of-example-file, result-disabled js-disabled) 33 | ``` 34 | 35 | ```md 36 | 37 | 38 | @[example](name-of-example-file, playground-only) 39 | ``` 40 | 41 | This 2nd argument is actually a list of props. For complete full list of available props, see the [`vue-example-simple`](https://github.com/chrisvfritz/vuepress-enhanced-examples/blob/master/.vuepress/components/vue-example-simple.vue) component. 42 | 43 | ## Features 44 | 45 | ### Consumer experience 46 | 47 | - **JS experience scaling**: By default, all JS examples render ES5, but more experienced devs can be toggle to modern JS by clicking on the `js (es5)` label in the top-right corner of JS code blocks. 48 | 49 | - **HTML experience scaling**: By default, all HTML examples render `v-bind:` and `v-on:` instead of their shorthands: `:` and `@`, respectively. This can also be toggled by clicking on the `html` label in the top-right corner of HTML code blocks. 50 | 51 | - **Selective sandboxing**: All examples can be rendered into individual iframes, so that no code on the page or from other examples can interfere with them. Upon rendering an example, however, we check if the iframe contains a top-level `app` variable with a Vue instance. If it does, we assign the value of `app` to `app1`, `app2`, or whatever the first numbered app variable is in the parent window. Then we display the name of this variable to the user in the top-right corner of the iframe. This way, consumers can still open the console and type `app1.message = 'new message'` to see something work. These results also always use ES5, to ensure browser compatibility. 52 | 53 | - **Automatic CodePen generation**: CodePens for all examples are automatically generated, using the code style users have selected and automatically hiding panels when no code for that panel exists. 54 | 55 | ### Author experience 56 | 57 | Since examples are all in `.vue` files, they can receive: 58 | 59 | - **Syntax highlighting**: For HTML, CSS, and JS(X). 60 | - **Linting**: ESLint and Stylelint can parse `.vue` files. 61 | - **Formatting**: Prettier can format JS and CSS. 62 | 63 | Since examples are processed at build time, we can also use: 64 | 65 | - **Advanced JS features**: Write examples in terser ES2015+, with automatic compilation to a readable ES5 alternative. 66 | - **Custom render formatting**: You can write your examples in one format, while displaying them in another, because formatting with Prettier also happens during example compilation, using its own configuration. 67 | - **Automatic result rendering**: No more repetition! Also, since all results are in iframes, we no longer have to manually use `id="app1"`, `id="app2"`, etc. When crafting an example, you don't have to think outside that example. 68 | - **Automatic CodePen generation**: No more maintaining examples under a specific user's account, that only they can update. 69 | 70 | And finally, because examples are referenced, rather than embedded directly in markdown, we also get: 71 | 72 | - **Dynamically reusability**: When you want to reference an example multiple times, or only show pieces of it at a time, interspersed between other text. 73 | 74 | ## Demos 75 | 76 | Check out a [live demo here](http://vuepress-enhanced-examples.surge.sh/demos/intro.html). 77 | -------------------------------------------------------------------------------- /aliases.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | function resolveSrc(_path) { 4 | return path.join(__dirname, _path) 5 | } 6 | 7 | const aliases = { 8 | '@store': '.vuepress/store.js', 9 | } 10 | 11 | module.exports = { 12 | webpack: {}, 13 | } 14 | 15 | for (const alias in aliases) { 16 | module.exports.webpack[alias] = resolveSrc(aliases[alias]) 17 | } 18 | -------------------------------------------------------------------------------- /demos/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ## What is Vue.js? 4 | 5 | Vue (pronounced /vjuː/, like **view**) is a **progressive framework** for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with [modern tooling](single-file-components.html) and [supporting libraries](https://github.com/vuejs/awesome-vue#components--libraries). 6 | 7 | If you’d like to learn more about Vue before diving in, we created a video walking through the core principles and a sample project. 8 | 9 | If you are an experienced frontend developer and want to know how Vue compares to other libraries/frameworks, check out the [Comparison with Other Frameworks](comparison.html). 10 | 11 | ## Getting Started 12 | 13 |

The official guide assumes intermediate level knowledge of HTML, CSS, and JavaScript. If you are totally new to frontend development, it might not be the best idea to jump right into a framework as your first step - grasp the basics then come back! Prior experience with other frameworks helps, but is not required.

14 | 15 | The easiest way to try out Vue.js is using the [JSFiddle Hello World example](https://jsfiddle.net/chrisvfritz/50wL7mdz/). Feel free to open it in another tab and follow along as we go through some basic examples. Or, you can create an index.html file and include Vue with: 16 | 17 | @[example](script-dev, html-only) 18 | 19 | or: 20 | 21 | @[example](script-prod, html-only) 22 | 23 | The [Installation](installation.html) page provides more options of installing Vue. Note: We **do not** recommend that beginners start with `vue-cli`, especially if you are not yet familiar with Node.js-based build tools. 24 | 25 | ## Declarative Rendering 26 | 27 | At the core of Vue.js is a system that enables us to declaratively render data to the DOM using straightforward template syntax: 28 | 29 | @[example](intro-hello-world) 30 | 31 | We have already created our very first Vue app! This looks pretty similar to rendering a string template, but Vue has done a lot of work under the hood. The data and the DOM are now linked, and everything is now **reactive**. How do we know? Open your browser's JavaScript console (right now, on this page) and set `app.message` to a different value. You should see the rendered example above update accordingly. 32 | 33 | In addition to text interpolation, we can also bind element attributes like this: 34 | 35 | @[example](intro-attribute-binding) 36 | 37 | Here we are encountering something new. The `v-bind` attribute you are seeing is called a **directive**. Directives are prefixed with `v-` to indicate that they are special attributes provided by Vue, and as you may have guessed, they apply special reactive behavior to the rendered DOM. Here, it is basically saying "keep this element's `title` attribute up-to-date with the `message` property on the Vue instance." 38 | 39 | If you open up your JavaScript console again and enter `app.message = 'some new message'`, you'll once again see that the bound HTML - in this case the `title` attribute - has been updated. 40 | 41 | ## Conditionals and Loops 42 | 43 | It's easy to toggle the presence of an element, too: 44 | 45 | @[example](intro-conditional) 46 | 47 | Go ahead and enter `app.seen = false` in the console. You should see the message disappear. 48 | 49 | This example demonstrates that we can bind data to not only text and attributes, but also the **structure** of the DOM. Moreover, Vue also provides a powerful transition effect system that can automatically apply [transition effects](transitions.html) when elements are inserted/updated/removed by Vue. 50 | 51 | There are quite a few other directives, each with its own special functionality. For example, the `v-for` directive can be used for displaying a list of items using the data from an Array: 52 | 53 | @[example](intro-loop) 54 | 55 | In the console, enter `app.todos.push({ text: 'New item' })`. You should see a new item appended to the list. 56 | 57 | ## Handling User Input 58 | 59 | To let users interact with your app, we can use the `v-on` directive to attach event listeners that invoke methods on our Vue instances: 60 | 61 | @[example](intro-method) 62 | 63 | Note that in this method we update the state of our app without touching the DOM - all DOM manipulations are handled by Vue, and the code you write is focused on the underlying logic. 64 | 65 | Vue also provides the `v-model` directive that makes two-way binding between form input and app state a breeze: 66 | 67 | @[example](intro-v-model) 68 | 69 | ## Composing with Components 70 | 71 | The component system is another important concept in Vue, because it's an abstraction that allows us to build large-scale applications composed of small, self-contained, and often reusable components. If we think about it, almost any type of application interface can be abstracted into a tree of components: 72 | 73 | ![Component Tree](/images/components.png) 74 | 75 | In Vue, a component is essentially a Vue instance with pre-defined options. Registering a component in Vue is straightforward: 76 | 77 | @[example](intro-component, js-only) 78 | 79 | Now you can compose it in another component's template: 80 | 81 | @[example](intro-component, html-only) 82 | 83 | But this would render the same text for every todo, which is not super interesting. We should be able to pass data from the parent scope into child components. Let's modify the component definition to make it accept a [prop](components.html#Props): 84 | 85 | @[example](intro-component-with-prop, js-only) 86 | 87 | Now we can pass the todo into each repeated component using `v-bind`: 88 | 89 | @[example](intro-component-with-prop-complete) 90 | 91 | This is a contrived example, but we have managed to separate our app into two smaller units, and the child is reasonably well-decoupled from the parent via the props interface. We can now further improve our `` component with more complex template and logic without affecting the parent app. 92 | 93 | In a large application, it is necessary to divide the whole app into components to make development manageable. We will talk a lot more about components [later in the guide](components.html), but here's an (imaginary) example of what an app's template might look like with components: 94 | 95 | @[example](intro-components-nested, html-only) 96 | 97 | ### Relation to Custom Elements 98 | 99 | You may have noticed that Vue components are very similar to **Custom Elements**, which are part of the [Web Components Spec](https://www.w3.org/wiki/WebComponents/). That's because Vue's component syntax is loosely modeled after the spec. For example, Vue components implement the [Slot API](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Slots-Proposal.md) and the `is` special attribute. However, there are a few key differences: 100 | 101 | 1. The Web Components Spec is still in draft status, and is not natively implemented in every browser. In comparison, Vue components don't require any polyfills and work consistently in all supported browsers (IE9 and above). When needed, Vue components can also be wrapped inside a native custom element. 102 | 2. Vue components provide important features that are not available in plain custom elements, most notably cross-component data flow, custom event communication and build tool integrations. 103 | 104 | ## Ready for More? 105 | 106 | We've briefly introduced the most basic features of Vue.js core - the rest of this guide will cover them and other advanced features with much finer details, so make sure to read through it all! 107 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.js': ['eslint --fix', 'prettier --write', 'git add'], 3 | '*.json': ['prettier --write', 'git add'], 4 | '*.vue': ['eslint --fix', 'stylelint --fix', 'prettier --write', 'git add'], 5 | '*.scss': ['stylelint --fix', 'prettier --write', 'git add'], 6 | '*.md': ['markdownlint', 'prettier --write', 'git add'], 7 | '*.{png,jpeg,jpg,gif,svg}': ['imagemin-lint-staged', 'git add'], 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuepress-enhanced-examples", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "lint": "eslint --ext .js,.vue --fix . && stylelint --fix \"src/**/*.vue\" \"src/**/*.scss\" && markdownlint docs/*.md *.md && prettier --list-different --write \"**/*.{js,json,css,scss,vue,md}\"", 6 | "dev": "vuepress dev", 7 | "predeploy": "vuepress build", 8 | "deploy": "surge --project .vuepress/dist --domain vuepress-enhanced-examples.surge.sh" 9 | }, 10 | "gitHooks": { 11 | "pre-commit": "cross-env PRE_COMMIT=true lint-staged" 12 | }, 13 | "dependencies": { 14 | "@vue/component-compiler-utils": "2.0.x", 15 | "buble": "0.19.x", 16 | "markdown-it-custom-block": "0.1.x", 17 | "prettier": "1.13.x", 18 | "prismjs": "1.14.x", 19 | "strip-indent": "2.0.x", 20 | "vuepress": "0.10.x" 21 | }, 22 | "devDependencies": { 23 | "cross-env": "5.1.x", 24 | "eslint": "^4.19.1", 25 | "eslint-config-prettier": "^2.9.0", 26 | "eslint-plugin-node": "^6.0.1", 27 | "eslint-plugin-vue-libs": "^3.0.0", 28 | "imagemin-lint-staged": "0.3.x", 29 | "lint-staged": "7.1.x", 30 | "markdownlint-cli": "0.10.x", 31 | "node-sass": "4.9.x", 32 | "sass-loader": "7.0.x", 33 | "stylelint": "9.2.x", 34 | "stylelint-config-css-modules": "1.2.x", 35 | "stylelint-config-prettier": "3.2.x", 36 | "stylelint-config-recess-order": "2.0.x", 37 | "stylelint-config-standard": "18.2.x", 38 | "stylelint-scss": "3.1.x", 39 | "yorkie": "^1.0.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | // Use the Standard config as the base 4 | // https://github.com/stylelint/stylelint-config-standard 5 | 'stylelint-config-standard', 6 | // Enforce a standard order for CSS properties 7 | // https://github.com/stormwarning/stylelint-config-recess-order 8 | 'stylelint-config-recess-order', 9 | // Override rules that would interfere with Prettier 10 | // https://github.com/shannonmoeller/stylelint-config-prettier 11 | 'stylelint-config-prettier', 12 | // Override rules to allow linting of CSS modules 13 | // https://github.com/pascalduez/stylelint-config-css-modules 14 | 'stylelint-config-css-modules', 15 | ], 16 | plugins: [ 17 | // Bring in some extra rules for SCSS 18 | 'stylelint-scss', 19 | ], 20 | // Rule lists: 21 | // - https://stylelint.io/user-guide/rules/ 22 | // - https://github.com/kristerkari/stylelint-scss#list-of-rules 23 | rules: { 24 | // Allow newlines inside class attribute values 25 | 'string-no-newline': null, 26 | // Enforce camelCase for classes and ids, to work better 27 | // with CSS modules 28 | 'selector-class-pattern': /^[a-z0-9][a-zA-Z0-9]*(-(enter|leave)(-(active|to))?)?$/, 29 | 'selector-id-pattern': /^[a-z0-9][a-zA-Z0-9]*$/, 30 | // Limit the number of universal selectors in a selector, 31 | // to avoid very slow selectors 32 | 'selector-max-universal': 1, 33 | // Disallow allow global element/type selectors in scoped modules 34 | 'selector-max-type': [0, { ignore: ['child', 'descendant', 'compounded'] }], 35 | // === 36 | // PRETTIER 37 | // === 38 | // HACK: to compensate for https://github.com/shannonmoeller/stylelint-config-prettier/issues/4 39 | // Modifying setting from Standard: https://github.com/stylelint/stylelint-config-standard/blob/7b76d7d0060f2e13a331806a09c2096c7536b0a6/index.js#L6 40 | 'at-rule-empty-line-before': [ 41 | 'always', 42 | { 43 | except: ['blockless-after-same-name-blockless', 'first-nested'], 44 | ignore: ['after-comment'], 45 | ignoreAtRules: ['else'], 46 | }, 47 | ], 48 | // === 49 | // SCSS 50 | // === 51 | 'scss/dollar-variable-colon-space-after': 'always', 52 | 'scss/dollar-variable-colon-space-before': 'never', 53 | 'scss/dollar-variable-no-missing-interpolation': true, 54 | 'scss/dollar-variable-pattern': /^[a-z-]+$/, 55 | 'scss/double-slash-comment-whitespace-inside': 'always', 56 | 'scss/operator-no-newline-before': true, 57 | 'scss/operator-no-unspaced': true, 58 | 'scss/selector-no-redundant-nesting-selector': true, 59 | // Allow SCSS and CSS module keywords beginning with `@` 60 | 'at-rule-no-unknown': null, 61 | 'scss/at-rule-no-unknown': true, 62 | }, 63 | } 64 | --------------------------------------------------------------------------------