style: format html&markdown files (#11531)

This commit is contained in:
Kevin Deng 三咲智子 2024-08-07 10:57:18 +08:00 committed by GitHub
parent 7c75cc3988
commit 8a99f903db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 1445 additions and 1313 deletions

View File

@ -44,7 +44,7 @@ This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
### Full Message Format ### Full Message Format
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**: A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
``` ```
<type>(<scope>): <subject> <type>(<scope>): <subject>
@ -74,9 +74,9 @@ The scope could be anything specifying the place of the commit change. For examp
The subject contains a succinct description of the change: The subject contains a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes" - use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter - don't capitalize the first letter
* no dot (.) at the end - no dot (.) at the end
### Body ### Body

View File

@ -35,7 +35,6 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before
Another aspect of it is that large scale stylistic changes result in massive diffs that touch multiple files, adding noise to the git history and makes tracing behavior changes across commits more cumbersome. Another aspect of it is that large scale stylistic changes result in massive diffs that touch multiple files, adding noise to the git history and makes tracing behavior changes across commits more cumbersome.
### Pull Request Checklist ### Pull Request Checklist
- Vue core has two primary work branches: `main` and `minor`. - Vue core has two primary work branches: `main` and `minor`.

View File

@ -80,6 +80,7 @@ Depending on the type of the PR, different considerations need to be taken into
- Make sure it doesn't accidentally cause dev-only or compiler-only code branches to be included in the runtime build. Notable case is that some functions in @vue/shared are compiler-only and should not be used in runtime code, e.g. `isHTMLTag` and `isSVGTag`. - Make sure it doesn't accidentally cause dev-only or compiler-only code branches to be included in the runtime build. Notable case is that some functions in @vue/shared are compiler-only and should not be used in runtime code, e.g. `isHTMLTag` and `isSVGTag`.
- Performance - Performance
- Be careful about code changes in "hot paths", in particular the Virtual DOM renderer (`runtime-core/src/renderer.ts`) and component instantiation code. - Be careful about code changes in "hot paths", in particular the Virtual DOM renderer (`runtime-core/src/renderer.ts`) and component instantiation code.
- Potential Breakage - Potential Breakage

View File

@ -1,4 +1,3 @@
dist dist
*.md
*.html
pnpm-lock.yaml pnpm-lock.yaml
CHANGELOG*.md

View File

@ -10,6 +10,6 @@ Please note that we do not consider XSS via template expressions a valid attack
We would like to thank the following security researchers for responsibly disclosing security issues to us. We would like to thank the following security researchers for responsibly disclosing security issues to us.
- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290 ) - Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290)
- Mix - [@mnixry](https://github.com/mnixry) - Mix - [@mnixry](https://github.com/mnixry)
- Aviv Keller - [@RedYetiDev](https://github.com/redyetidev) | [LinkedIn](https://www.linkedin.com/in/redyetidev) <redyetidev@gmail.com> - Aviv Keller - [@RedYetiDev](https://github.com/redyetidev) | [LinkedIn](https://www.linkedin.com/in/redyetidev) <redyetidev@gmail.com>

View File

@ -13,7 +13,7 @@ const { render, createApp } = createRenderer({
patchProp, patchProp,
insert, insert,
remove, remove,
createElement createElement,
// ... // ...
}) })

View File

@ -1,12 +1,12 @@
# @vue/runtime-dom # @vue/runtime-dom
``` js ```js
import { h, createApp } from '@vue/runtime-dom' import { h, createApp } from '@vue/runtime-dom'
const RootComponent = { const RootComponent = {
render() { render() {
return h('div', 'hello world') return h('div', 'hello world')
} },
} }
createApp(RootComponent).mount('#app') createApp(RootComponent).mount('#app')

View File

@ -4,7 +4,7 @@ This is for Vue's own internal tests only - it ensures logic tested using this p
It can also be used as a reference for implementing a custom renderer. It can also be used as a reference for implementing a custom renderer.
``` js ```js
import { h, render, nodeOps, dumpOps } from '@vue/runtime-test' import { h, render, nodeOps, dumpOps } from '@vue/runtime-test'
const App = { const App = {

View File

@ -11,7 +11,7 @@
```ts ```ts
function renderToString( function renderToString(
input: App | VNode, input: App | VNode,
context?: SSRContext context?: SSRContext,
): Promise<string> ): Promise<string>
``` ```
@ -23,7 +23,7 @@ const { renderToString } = require('@vue/server-renderer')
const app = createSSRApp({ const app = createSSRApp({
data: () => ({ msg: 'hello' }), data: () => ({ msg: 'hello' }),
template: `<div>{{ msg }}</div>` template: `<div>{{ msg }}</div>`,
}) })
;(async () => { ;(async () => {
@ -74,7 +74,7 @@ Render and pipe to an existing [Node.js Writable stream](https://nodejs.org/api/
function pipeToNodeWritable( function pipeToNodeWritable(
input: App | VNode, input: App | VNode,
context: SSRContext = {}, context: SSRContext = {},
writable: Writable writable: Writable,
): void ): void
``` ```
@ -94,7 +94,7 @@ Renders input as a [Web ReadableStream](https://developer.mozilla.org/en-US/docs
```ts ```ts
function renderToWebStream( function renderToWebStream(
input: App | VNode, input: App | VNode,
context?: SSRContext context?: SSRContext,
): ReadableStream ): ReadableStream
``` ```
@ -117,7 +117,7 @@ Render and pipe to an existing [Web WritableStream](https://developer.mozilla.or
function pipeToWebWritable( function pipeToWebWritable(
input: App | VNode, input: App | VNode,
context: SSRContext = {}, context: SSRContext = {},
writable: WritableStream writable: WritableStream,
): void ): void
``` ```
@ -144,7 +144,7 @@ Renders input in streaming mode using a simple readable interface.
function renderToSimpleStream( function renderToSimpleStream(
input: App | VNode, input: App | VNode,
context: SSRContext, context: SSRContext,
options: SimpleReadable options: SimpleReadable,
): SimpleReadable ): SimpleReadable
interface SimpleReadable { interface SimpleReadable {
@ -172,7 +172,7 @@ renderToSimpleStream(
}, },
destroy(err) { destroy(err) {
// error encountered // error encountered
} },
} },
) )
``` ```

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@ -7,13 +7,16 @@
<link rel="icon" type="image/svg" href="/logo.svg" /> <link rel="icon" type="image/svg" href="/logo.svg" />
<title>Vue SFC Playground</title> <title>Vue SFC Playground</title>
<script> <script>
const savedPreferDark = localStorage.getItem('vue-sfc-playground-prefer-dark') const savedPreferDark = localStorage.getItem(
if ( 'vue-sfc-playground-prefer-dark',
savedPreferDark === 'true' || )
(!savedPreferDark && window.matchMedia('(prefers-color-scheme: dark)').matches) if (
) { savedPreferDark === 'true' ||
(!savedPreferDark &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark') document.documentElement.classList.add('dark')
} }
</script> </script>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</head> </head>

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />

View File

@ -1,6 +1,10 @@
<title>Vue Template Explorer</title> <title>Vue Template Explorer</title>
<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://unpkg.com/monaco-editor@0.20.0/min/vs/editor/editor.main.css"> <link
<link rel="stylesheet" href="./style.css"> rel="stylesheet"
data-name="vs/editor/editor.main"
href="https://unpkg.com/monaco-editor@0.20.0/min/vs/editor/editor.main.css"
/>
<link rel="stylesheet" href="./style.css" />
<div id="header"></div> <div id="header"></div>
<div id="source" class="editor"></div> <div id="source" class="editor"></div>
@ -8,13 +12,13 @@
<script src="https://unpkg.com/monaco-editor@0.20.0/min/vs/loader.js"></script> <script src="https://unpkg.com/monaco-editor@0.20.0/min/vs/loader.js"></script>
<script> <script>
require.config({ require.config({
paths: { paths: {
'vs': 'https://unpkg.com/monaco-editor@0.20.0/min/vs' vs: 'https://unpkg.com/monaco-editor@0.20.0/min/vs',
} },
}) })
</script> </script>
<script src="./dist/template-explorer.global.js"></script> <script src="./dist/template-explorer.global.js"></script>
<script> <script>
require(['vs/editor/editor.main'], init /* injected by build */) require(['vs/editor/editor.main'], init /* injected by build */)
</script> </script>

View File

@ -1,6 +1,10 @@
<title>Vue Template Explorer</title> <title>Vue Template Explorer</title>
<link rel="stylesheet" data-name="vs/editor/editor.main" href="./node_modules/monaco-editor/min/vs/editor/editor.main.css"> <link
<link rel="stylesheet" href="./style.css"> rel="stylesheet"
data-name="vs/editor/editor.main"
href="./node_modules/monaco-editor/min/vs/editor/editor.main.css"
/>
<link rel="stylesheet" href="./style.css" />
<div id="header"></div> <div id="header"></div>
<div id="source" class="editor"></div> <div id="source" class="editor"></div>
@ -8,13 +12,13 @@
<script src="./node_modules/monaco-editor/min/vs/loader.js"></script> <script src="./node_modules/monaco-editor/min/vs/loader.js"></script>
<script> <script>
require.config({ require.config({
paths: { paths: {
'vs': './node_modules/monaco-editor/min/vs' vs: './node_modules/monaco-editor/min/vs',
} },
}) })
</script> </script>
<script src="./dist/template-explorer.global.js"></script> <script src="./dist/template-explorer.global.js"></script>
<script> <script>
require(['vs/editor/editor.main'], init /* injected by build */) require(['vs/editor/editor.main'], init /* injected by build */)
</script> </script>

View File

@ -84,12 +84,12 @@ The following workflow walks through the steps of migrating an actual Vue 2 app
...options, ...options,
compilerOptions: { compilerOptions: {
compatConfig: { compatConfig: {
MODE: 2 MODE: 2,
} },
} },
} }
}) })
} },
} }
``` ```
@ -103,8 +103,8 @@ The following workflow walks through the steps of migrating an actual Vue 2 app
module.exports = { module.exports = {
resolve: { resolve: {
alias: { alias: {
vue: '@vue/compat' vue: '@vue/compat',
} },
}, },
module: { module: {
rules: [ rules: [
@ -114,13 +114,13 @@ The following workflow walks through the steps of migrating an actual Vue 2 app
options: { options: {
compilerOptions: { compilerOptions: {
compatConfig: { compatConfig: {
MODE: 2 MODE: 2,
} },
} },
} },
} },
] ],
} },
} }
``` ```
@ -134,20 +134,20 @@ The following workflow walks through the steps of migrating an actual Vue 2 app
export default { export default {
resolve: { resolve: {
alias: { alias: {
vue: '@vue/compat' vue: '@vue/compat',
} },
}, },
plugins: [ plugins: [
vue({ vue({
template: { template: {
compilerOptions: { compilerOptions: {
compatConfig: { compatConfig: {
MODE: 2 MODE: 2,
} },
} },
} },
}) }),
] ],
} }
``` ```
@ -207,7 +207,7 @@ import { configureCompat } from 'vue'
// disable compat for certain features // disable compat for certain features
configureCompat({ configureCompat({
FEATURE_ID_A: false, FEATURE_ID_A: false,
FEATURE_ID_B: false FEATURE_ID_B: false,
}) })
``` ```
@ -221,7 +221,7 @@ import { configureCompat } from 'vue'
configureCompat({ configureCompat({
MODE: 3, MODE: 3,
FEATURE_ID_A: true, FEATURE_ID_A: true,
FEATURE_ID_B: true FEATURE_ID_B: true,
}) })
``` ```
@ -233,8 +233,8 @@ A component can use the `compatConfig` option, which expects the same options as
export default { export default {
compatConfig: { compatConfig: {
MODE: 3, // opt-in to Vue 3 behavior for this component only MODE: 3, // opt-in to Vue 3 behavior for this component only
FEATURE_ID_A: true // features can also be toggled at component level FEATURE_ID_A: true, // features can also be toggled at component level
} },
// ... // ...
} }
``` ```
@ -256,73 +256,73 @@ Features that start with `COMPILER_` are compiler-specific: if you are using the
> Should be fixed upfront or will likely lead to errors > Should be fixed upfront or will likely lead to errors
| ID | Type | Description | Docs | | ID | Type | Description | Docs |
| ------------------------------------- | ---- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | ------------------------------------- | ---- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| GLOBAL_MOUNT_CONTAINER | ⨂ | Mounted application does not replace the element it's mounted to | [link](https://v3-migration.vuejs.org/breaking-changes/mount-changes.html) | | GLOBAL_MOUNT_CONTAINER | ⨂ | Mounted application does not replace the element it's mounted to | [link](https://v3-migration.vuejs.org/breaking-changes/mount-changes.html) |
| CONFIG_DEVTOOLS | ⨂ | production devtools is now a build-time flag | [link](https://github.com/vuejs/core/tree/main/packages/vue#bundler-build-feature-flags) | | CONFIG_DEVTOOLS | ⨂ | production devtools is now a build-time flag | [link](https://github.com/vuejs/core/tree/main/packages/vue#bundler-build-feature-flags) |
| COMPILER_V_IF_V_FOR_PRECEDENCE | ⨂ | `v-if` and `v-for` precedence when used on the same element has changed | [link](https://v3-migration.vuejs.org/breaking-changes/v-if-v-for.html) | | COMPILER_V_IF_V_FOR_PRECEDENCE | ⨂ | `v-if` and `v-for` precedence when used on the same element has changed | [link](https://v3-migration.vuejs.org/breaking-changes/v-if-v-for.html) |
| COMPILER_V_IF_SAME_KEY | ⨂ | `v-if` branches can no longer have the same key | [link](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#on-conditional-branches) | | COMPILER_V_IF_SAME_KEY | ⨂ | `v-if` branches can no longer have the same key | [link](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#on-conditional-branches) |
| COMPILER_V_FOR_TEMPLATE_KEY_PLACEMENT | ⨂ | `<template v-for>` key should now be placed on `<template>` | [link](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for) | | COMPILER_V_FOR_TEMPLATE_KEY_PLACEMENT | ⨂ | `<template v-for>` key should now be placed on `<template>` | [link](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for) |
| COMPILER_SFC_FUNCTIONAL | ⨂ | `<template functional>` is no longer supported in SFCs | [link](https://v3-migration.vuejs.org/breaking-changes/functional-components.html#single-file-components-sfcs) | | | | COMPILER_SFC_FUNCTIONAL | ⨂ | `<template functional>` is no longer supported in SFCs | [link](https://v3-migration.vuejs.org/breaking-changes/functional-components.html#single-file-components-sfcs) |
### Partially Compatible with Caveats ### Partially Compatible with Caveats
| ID | Type | Description | Docs | | ID | Type | Description | Docs |
| ------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | | ------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| CONFIG_IGNORED_ELEMENTS | ◐ | `config.ignoredElements` is now `config.compilerOptions.isCustomElement` (only in browser compiler build). If using build setup, `isCustomElement` must be passed via build configuration. | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#config-ignoredelements-is-now-config-iscustomelement) | | CONFIG_IGNORED_ELEMENTS | ◐ | `config.ignoredElements` is now `config.compilerOptions.isCustomElement` (only in browser compiler build). If using build setup, `isCustomElement` must be passed via build configuration. | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#config-ignoredelements-is-now-config-iscustomelement) |
| COMPILER_INLINE_TEMPLATE | ◐ | `inline-template` removed (compat only supported in browser compiler build) | [link](https://v3-migration.vuejs.org/breaking-changes/inline-template-attribute.html) | | COMPILER_INLINE_TEMPLATE | ◐ | `inline-template` removed (compat only supported in browser compiler build) | [link](https://v3-migration.vuejs.org/breaking-changes/inline-template-attribute.html) |
| PROPS_DEFAULT_THIS | ◐ | props default factory no longer have access to `this` (in compat mode, `this` is not a real instance - it only exposes props, `$options` and injections) | [link](https://v3-migration.vuejs.org/breaking-changes/props-default-this.html) | | PROPS_DEFAULT_THIS | ◐ | props default factory no longer have access to `this` (in compat mode, `this` is not a real instance - it only exposes props, `$options` and injections) | [link](https://v3-migration.vuejs.org/breaking-changes/props-default-this.html) |
| INSTANCE_DESTROY | ◐ | `$destroy` instance method removed (in compat mode, only supported on root instance) | | | INSTANCE_DESTROY | ◐ | `$destroy` instance method removed (in compat mode, only supported on root instance) | |
| GLOBAL_PRIVATE_UTIL | ◐ | `Vue.util` is private and no longer available | | | GLOBAL_PRIVATE_UTIL | ◐ | `Vue.util` is private and no longer available | |
| CONFIG_PRODUCTION_TIP | ◐ | `config.productionTip` no longer necessary | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#config-productiontip-removed) | | CONFIG_PRODUCTION_TIP | ◐ | `config.productionTip` no longer necessary | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#config-productiontip-removed) |
| CONFIG_SILENT | ◐ | `config.silent` removed | | CONFIG_SILENT | ◐ | `config.silent` removed |
### Compat only (no warning) ### Compat only (no warning)
| ID | Type | Description | Docs | | ID | Type | Description | Docs |
| ------------------ | ---- | ------------------------------------- | ---------------------------------------- | | ------------------ | ---- | -------------------------------------- | ----------------------------------------------------------------------- |
| TRANSITION_CLASSES | ⭘ | Transition enter/leave classes changed | [link](https://v3-migration.vuejs.org/breaking-changes/transition.html) | | TRANSITION_CLASSES | ⭘ | Transition enter/leave classes changed | [link](https://v3-migration.vuejs.org/breaking-changes/transition.html) |
### Fully Compatible ### Fully Compatible
| ID | Type | Description | Docs | | ID | Type | Description | Docs |
| ---------------------------- | ---- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | ---------------------------- | ---- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| GLOBAL_MOUNT | ✔ | new Vue() -> createApp | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#mounting-app-instance) | | GLOBAL_MOUNT | ✔ | new Vue() -> createApp | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#mounting-app-instance) |
| GLOBAL_EXTEND | ✔ | Vue.extend removed (use `defineComponent` or `extends` option) | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-extend-replaced-by-definecomponent) | | GLOBAL_EXTEND | ✔ | Vue.extend removed (use `defineComponent` or `extends` option) | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-extend-replaced-by-definecomponent) |
| GLOBAL_PROTOTYPE | ✔ | `Vue.prototype` -> `app.config.globalProperties` | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-prototype-replaced-by-config-globalproperties) | | GLOBAL_PROTOTYPE | ✔ | `Vue.prototype` -> `app.config.globalProperties` | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-prototype-replaced-by-config-globalproperties) |
| GLOBAL_SET | ✔ | `Vue.set` removed (no longer needed) | | | GLOBAL_SET | ✔ | `Vue.set` removed (no longer needed) | |
| GLOBAL_DELETE | ✔ | `Vue.delete` removed (no longer needed) | | | GLOBAL_DELETE | ✔ | `Vue.delete` removed (no longer needed) | |
| GLOBAL_OBSERVABLE | ✔ | `Vue.observable` removed (use `reactive`) | [link](https://vuejs.org/api/reactivity-core.html#reactive) | | GLOBAL_OBSERVABLE | ✔ | `Vue.observable` removed (use `reactive`) | [link](https://vuejs.org/api/reactivity-core.html#reactive) |
| CONFIG_KEY_CODES | ✔ | config.keyCodes removed | [link](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) | | CONFIG_KEY_CODES | ✔ | config.keyCodes removed | [link](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) |
| CONFIG_WHITESPACE | ✔ | In Vue 3 whitespace defaults to `"condense"` | | | CONFIG_WHITESPACE | ✔ | In Vue 3 whitespace defaults to `"condense"` | |
| INSTANCE_SET | ✔ | `vm.$set` removed (no longer needed) | | | INSTANCE_SET | ✔ | `vm.$set` removed (no longer needed) | |
| INSTANCE_DELETE | ✔ | `vm.$delete` removed (no longer needed) | | | INSTANCE_DELETE | ✔ | `vm.$delete` removed (no longer needed) | |
| INSTANCE_EVENT_EMITTER | ✔ | `vm.$on`, `vm.$off`, `vm.$once` removed | [link](https://v3-migration.vuejs.org/breaking-changes/events-api.html) | | INSTANCE_EVENT_EMITTER | ✔ | `vm.$on`, `vm.$off`, `vm.$once` removed | [link](https://v3-migration.vuejs.org/breaking-changes/events-api.html) |
| INSTANCE_EVENT_HOOKS | ✔ | Instance no longer emits `hook:x` events | [link](https://v3-migration.vuejs.org/breaking-changes/vnode-lifecycle-events.html) | | INSTANCE_EVENT_HOOKS | ✔ | Instance no longer emits `hook:x` events | [link](https://v3-migration.vuejs.org/breaking-changes/vnode-lifecycle-events.html) |
| INSTANCE_CHILDREN | ✔ | `vm.$children` removed | [link](https://v3-migration.vuejs.org/breaking-changes/children.html) | | INSTANCE_CHILDREN | ✔ | `vm.$children` removed | [link](https://v3-migration.vuejs.org/breaking-changes/children.html) |
| INSTANCE_LISTENERS | ✔ | `vm.$listeners` removed | [link](https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html) | | INSTANCE_LISTENERS | ✔ | `vm.$listeners` removed | [link](https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html) |
| INSTANCE_SCOPED_SLOTS | ✔ | `vm.$scopedSlots` removed; `vm.$slots` now exposes functions | [link](https://v3-migration.vuejs.org/breaking-changes/slots-unification.html) | | INSTANCE_SCOPED_SLOTS | ✔ | `vm.$scopedSlots` removed; `vm.$slots` now exposes functions | [link](https://v3-migration.vuejs.org/breaking-changes/slots-unification.html) |
| INSTANCE_ATTRS_CLASS_STYLE | ✔ | `$attrs` now includes `class` and `style` | [link](https://v3-migration.vuejs.org/breaking-changes/attrs-includes-class-style.html) | | INSTANCE_ATTRS_CLASS_STYLE | ✔ | `$attrs` now includes `class` and `style` | [link](https://v3-migration.vuejs.org/breaking-changes/attrs-includes-class-style.html) |
| OPTIONS_DATA_FN | ✔ | `data` must be a function in all cases | [link](https://v3-migration.vuejs.org/breaking-changes/data-option.html) | | OPTIONS_DATA_FN | ✔ | `data` must be a function in all cases | [link](https://v3-migration.vuejs.org/breaking-changes/data-option.html) |
| OPTIONS_DATA_MERGE | ✔ | `data` from mixin or extension is now shallow merged | [link](https://v3-migration.vuejs.org/breaking-changes/data-option.html) | | OPTIONS_DATA_MERGE | ✔ | `data` from mixin or extension is now shallow merged | [link](https://v3-migration.vuejs.org/breaking-changes/data-option.html) |
| OPTIONS_BEFORE_DESTROY | ✔ | `beforeDestroy` -> `beforeUnmount` | | | OPTIONS_BEFORE_DESTROY | ✔ | `beforeDestroy` -> `beforeUnmount` | |
| OPTIONS_DESTROYED | ✔ | `destroyed` -> `unmounted` | | | OPTIONS_DESTROYED | ✔ | `destroyed` -> `unmounted` | |
| WATCH_ARRAY | ✔ | watching an array no longer triggers on mutation unless deep | [link](https://v3-migration.vuejs.org/breaking-changes/watch.html) | | WATCH_ARRAY | ✔ | watching an array no longer triggers on mutation unless deep | [link](https://v3-migration.vuejs.org/breaking-changes/watch.html) |
| V_ON_KEYCODE_MODIFIER | ✔ | `v-on` no longer supports keyCode modifiers | [link](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) | | V_ON_KEYCODE_MODIFIER | ✔ | `v-on` no longer supports keyCode modifiers | [link](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) |
| CUSTOM_DIR | ✔ | Custom directive hook names changed | [link](https://v3-migration.vuejs.org/breaking-changes/custom-directives.html) | | CUSTOM_DIR | ✔ | Custom directive hook names changed | [link](https://v3-migration.vuejs.org/breaking-changes/custom-directives.html) |
| ATTR_FALSE_VALUE | ✔ | No longer removes attribute if binding value is boolean `false` | [link](https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html) | | ATTR_FALSE_VALUE | ✔ | No longer removes attribute if binding value is boolean `false` | [link](https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html) |
| ATTR_ENUMERATED_COERCION | ✔ | No longer special case enumerated attributes | [link](https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html) | | ATTR_ENUMERATED_COERCION | ✔ | No longer special case enumerated attributes | [link](https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html) |
| TRANSITION_GROUP_ROOT | ✔ | `<transition-group>` no longer renders a root element by default | [link](https://v3-migration.vuejs.org/breaking-changes/transition-group.html) | | TRANSITION_GROUP_ROOT | ✔ | `<transition-group>` no longer renders a root element by default | [link](https://v3-migration.vuejs.org/breaking-changes/transition-group.html) |
| COMPONENT_ASYNC | ✔ | Async component API changed (now requires `defineAsyncComponent`) | [link](https://v3-migration.vuejs.org/breaking-changes/async-components.html) | | COMPONENT_ASYNC | ✔ | Async component API changed (now requires `defineAsyncComponent`) | [link](https://v3-migration.vuejs.org/breaking-changes/async-components.html) |
| COMPONENT_FUNCTIONAL | ✔ | Functional component API changed (now must be plain functions) | [link](https://v3-migration.vuejs.org/breaking-changes/functional-components.html) | | COMPONENT_FUNCTIONAL | ✔ | Functional component API changed (now must be plain functions) | [link](https://v3-migration.vuejs.org/breaking-changes/functional-components.html) |
| COMPONENT_V_MODEL | ✔ | Component v-model reworked | [link](https://v3-migration.vuejs.org/breaking-changes/v-model.html) | | COMPONENT_V_MODEL | ✔ | Component v-model reworked | [link](https://v3-migration.vuejs.org/breaking-changes/v-model.html) |
| RENDER_FUNCTION | ✔ | Render function API changed | [link](https://v3-migration.vuejs.org/breaking-changes/render-function-api.html) | | RENDER_FUNCTION | ✔ | Render function API changed | [link](https://v3-migration.vuejs.org/breaking-changes/render-function-api.html) |
| FILTERS | ✔ | Filters removed (this option affects only runtime filter APIs) | [link](https://v3-migration.vuejs.org/breaking-changes/filters.html) | | FILTERS | ✔ | Filters removed (this option affects only runtime filter APIs) | [link](https://v3-migration.vuejs.org/breaking-changes/filters.html) |
| COMPILER_IS_ON_ELEMENT | ✔ | `is` usage is now restricted to `<component>` only | [link](https://v3-migration.vuejs.org/breaking-changes/custom-elements-interop.html) | | COMPILER_IS_ON_ELEMENT | ✔ | `is` usage is now restricted to `<component>` only | [link](https://v3-migration.vuejs.org/breaking-changes/custom-elements-interop.html) |
| COMPILER_V_BIND_SYNC | ✔ | `v-bind.sync` replaced by `v-model` with arguments | [link](https://v3-migration.vuejs.org/breaking-changes/v-model.html) | | COMPILER_V_BIND_SYNC | ✔ | `v-bind.sync` replaced by `v-model` with arguments | [link](https://v3-migration.vuejs.org/breaking-changes/v-model.html) |
| COMPILER_V_BIND_PROP | ✔ | `v-bind.prop` modifier removed | | | COMPILER_V_BIND_PROP | ✔ | `v-bind.prop` modifier removed | |
| COMPILER_V_BIND_OBJECT_ORDER | ✔ | `v-bind="object"` is now order sensitive | [link](https://v3-migration.vuejs.org/breaking-changes/v-bind.html) | | COMPILER_V_BIND_OBJECT_ORDER | ✔ | `v-bind="object"` is now order sensitive | [link](https://v3-migration.vuejs.org/breaking-changes/v-bind.html) |
| COMPILER_V_ON_NATIVE | ✔ | `v-on.native` modifier removed | [link](https://v3-migration.vuejs.org/breaking-changes/v-on-native-modifier-removed.html) | | COMPILER_V_ON_NATIVE | ✔ | `v-on.native` modifier removed | [link](https://v3-migration.vuejs.org/breaking-changes/v-on-native-modifier-removed.html) |
| COMPILER_V_FOR_REF | ✔ | `ref` in `v-for` (compiler support) | | | COMPILER_V_FOR_REF | ✔ | `ref` in `v-for` (compiler support) | |
| COMPILER_NATIVE_TEMPLATE | ✔ | `<template>` with no special directives now renders as native element | | | COMPILER_NATIVE_TEMPLATE | ✔ | `<template>` with no special directives now renders as native element | |
| COMPILER_FILTERS | ✔ | filters (compiler support) | | | COMPILER_FILTERS | ✔ | filters (compiler support) | |

View File

@ -5,8 +5,9 @@
### From CDN or without a Bundler ### From CDN or without a Bundler
- **`vue(.runtime).global(.prod).js`**: - **`vue(.runtime).global(.prod).js`**:
- For direct use via `<script src="...">` in the browser. Exposes the `Vue` global. - For direct use via `<script src="...">` in the browser. Exposes the `Vue` global.
- Note that global builds are not [UMD](https://github.com/umdjs/umd) builds. They are built as [IIFEs](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) and is only meant for direct use via `<script src="...">`. - Note that global builds are not [UMD](https://github.com/umdjs/umd) builds. They are built as [IIFEs](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) and is only meant for direct use via `<script src="...">`.
- In-browser template compilation: - In-browser template compilation:
- **`vue.global.js`** is the "full" build that includes both the compiler and the runtime so it supports compiling templates on the fly. - **`vue.global.js`** is the "full" build that includes both the compiler and the runtime so it supports compiling templates on the fly.
- **`vue.runtime.global.js`** contains only the runtime and requires templates to be pre-compiled during a build step. - **`vue.runtime.global.js`** contains only the runtime and requires templates to be pre-compiled during a build step.
@ -38,10 +39,12 @@
`esm-bundler` builds of Vue expose global feature flags that can be overwritten at compile time: `esm-bundler` builds of Vue expose global feature flags that can be overwritten at compile time:
- `__VUE_OPTIONS_API__` - `__VUE_OPTIONS_API__`
- Default: `true` - Default: `true`
- Enable / disable Options API support - Enable / disable Options API support
- `__VUE_PROD_DEVTOOLS__` - `__VUE_PROD_DEVTOOLS__`
- Default: `false` - Default: `false`
- Enable / disable devtools support in production - Enable / disable devtools support in production

View File

@ -10,9 +10,14 @@
-webkit-transition: -webkit-transform 50ms ease; -webkit-transition: -webkit-transform 50ms ease;
transition: transform 50ms ease; transition: transform 50ms ease;
} }
.v-appear, .v-enter, .v-leave-active, .v-appear,
.test-appear, .test-enter, .test-leave-active, .v-enter,
.hello, .bye.active, .v-leave-active,
.test-appear,
.test-enter,
.test-leave-active,
.hello,
.bye.active,
.changed-enter { .changed-enter {
opacity: 0; opacity: 0;
} }
@ -33,19 +38,35 @@
-webkit-animation: test-leave 100ms; -webkit-animation: test-leave 100ms;
} }
@keyframes test-enter { @keyframes test-enter {
from { opacity: 0 } from {
to { opacity: 1 } opacity: 0;
}
to {
opacity: 1;
}
} }
@-webkit-keyframes test-enter { @-webkit-keyframes test-enter {
from { opacity: 0 } from {
to { opacity: 1 } opacity: 0;
}
to {
opacity: 1;
}
} }
@keyframes test-leave { @keyframes test-leave {
from { opacity: 1 } from {
to { opacity: 0 } opacity: 1;
}
to {
opacity: 0;
}
} }
@-webkit-keyframes test-leave { @-webkit-keyframes test-leave {
from { opacity: 1 } from {
to { opacity: 0 } opacity: 1;
}
to {
opacity: 0;
}
} }
</style> </style>

View File

@ -3,59 +3,68 @@
<div id="demo"> <div id="demo">
<h1>Latest Vue.js Commits</h1> <h1>Latest Vue.js Commits</h1>
<template v-for="branch in branches"> <template v-for="branch in branches">
<input type="radio" <input
type="radio"
:id="branch" :id="branch"
:value="branch" :value="branch"
name="branch" name="branch"
v-model="currentBranch"> v-model="currentBranch"
/>
<label :for="branch">{{ branch }}</label> <label :for="branch">{{ branch }}</label>
</template> </template>
<p>vuejs/core@{{ currentBranch }}</p> <p>vuejs/core@{{ currentBranch }}</p>
<ul> <ul>
<li v-for="{ html_url, sha, author, commit } in commits"> <li v-for="{ html_url, sha, author, commit } in commits">
<a :href="html_url" target="_blank" class="commit">{{ sha.slice(0, 7) }}</a> <a :href="html_url" target="_blank" class="commit"
- <span class="message">{{ truncate(commit.message) }}</span><br> >{{ sha.slice(0, 7) }}</a
by <span class="author"><a :href="author.html_url" target="_blank">{{ commit.author.name }}</a></span> >
- <span class="message">{{ truncate(commit.message) }}</span><br />
by
<span class="author"
><a :href="author.html_url" target="_blank"
>{{ commit.author.name }}</a
></span
>
at <span class="date">{{ formatDate(commit.author.date) }}</span> at <span class="date">{{ formatDate(commit.author.date) }}</span>
</li> </li>
</ul> </ul>
</div> </div>
<script> <script>
const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=` const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=`
Vue.createApp({ Vue.createApp({
data: () => ({ data: () => ({
branches: ['main', 'v2-compat'], branches: ['main', 'v2-compat'],
currentBranch: 'main', currentBranch: 'main',
commits: null commits: null,
}), }),
created() { created() {
this.fetchData() this.fetchData()
},
watch: {
currentBranch: 'fetchData'
},
methods: {
fetchData() {
fetch(`${API_URL}${this.currentBranch}`)
.then(res => res.json())
.then(data => {
this.commits = data
})
}, },
truncate(v) {
const newline = v.indexOf('\n') watch: {
return newline > 0 ? v.slice(0, newline) : v currentBranch: 'fetchData',
}, },
formatDate(v) {
return v.replace(/T|Z/g, ' ') methods: {
} fetchData() {
} fetch(`${API_URL}${this.currentBranch}`)
}).mount('#demo') .then(res => res.json())
.then(data => {
this.commits = data
})
},
truncate(v) {
const newline = v.indexOf('\n')
return newline > 0 ? v.slice(0, newline) : v
},
formatDate(v) {
return v.replace(/T|Z/g, ' ')
},
},
}).mount('#demo')
</script> </script>
<style> <style>
@ -70,7 +79,8 @@ Vue.createApp({
line-height: 1.5em; line-height: 1.5em;
margin-bottom: 20px; margin-bottom: 20px;
} }
.author, .date { .author,
.date {
font-weight: bold; font-weight: bold;
} }
</style> </style>

View File

@ -26,142 +26,141 @@
</script> </script>
<!-- DemoGrid component script --> <!-- DemoGrid component script -->
<script> <script>
const DemoGrid = { const DemoGrid = {
template: '#grid-template', template: '#grid-template',
props: { props: {
data: Array, data: Array,
columns: Array, columns: Array,
filterKey: String filterKey: String,
}, },
data() { data() {
return { return {
sortKey: '', sortKey: '',
sortOrders: this.columns.reduce((o, key) => (o[key] = 1, o), {}) sortOrders: this.columns.reduce((o, key) => ((o[key] = 1), o), {}),
} }
}, },
computed: { computed: {
filteredData() { filteredData() {
const sortKey = this.sortKey const sortKey = this.sortKey
const filterKey = this.filterKey && this.filterKey.toLowerCase() const filterKey = this.filterKey && this.filterKey.toLowerCase()
const order = this.sortOrders[sortKey] || 1 const order = this.sortOrders[sortKey] || 1
let data = this.data let data = this.data
if (filterKey) { if (filterKey) {
data = data.filter(row => { data = data.filter(row => {
return Object.keys(row).some(key => { return Object.keys(row).some(key => {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1 return String(row[key]).toLowerCase().indexOf(filterKey) > -1
}) })
}) })
} }
if (sortKey) { if (sortKey) {
data = data.slice().sort((a, b) => { data = data.slice().sort((a, b) => {
a = a[sortKey] a = a[sortKey]
b = b[sortKey] b = b[sortKey]
return (a === b ? 0 : a > b ? 1 : -1) * order return (a === b ? 0 : a > b ? 1 : -1) * order
}) })
} }
return data return data
} },
}, },
methods: { methods: {
sortBy(key) { sortBy(key) {
this.sortKey = key this.sortKey = key
this.sortOrders[key] = this.sortOrders[key] * -1 this.sortOrders[key] = this.sortOrders[key] * -1
},
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
},
}, },
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
} }
}
</script> </script>
<!-- App template (in DOM) --> <!-- App template (in DOM) -->
<div id="demo"> <div id="demo">
<form id="search"> <form id="search">Search <input name="query" v-model="searchQuery" /></form>
Search <input name="query" v-model="searchQuery"> <demo-grid :data="gridData" :columns="gridColumns" :filter-key="searchQuery">
</form>
<demo-grid
:data="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</demo-grid> </demo-grid>
</div> </div>
<!-- App script --> <!-- App script -->
<script> <script>
Vue.createApp({ Vue.createApp({
components: { components: {
DemoGrid DemoGrid,
}, },
data: () => ({ data: () => ({
searchQuery: '', searchQuery: '',
gridColumns: ['name', 'power'], gridColumns: ['name', 'power'],
gridData: [ gridData: [
{ name: 'Chuck Norris', power: Infinity }, { name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 }, { name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 }, { name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 } { name: 'Jet Li', power: 8000 },
] ],
}) }),
}).mount('#demo') }).mount('#demo')
</script> </script>
<style> <style>
body { body {
font-family: Helvetica Neue, Arial, sans-serif; font-family:
font-size: 14px; Helvetica Neue,
color: #444; Arial,
} sans-serif;
font-size: 14px;
color: #444;
}
table { table {
border: 2px solid #42b983; border: 2px solid #42b983;
border-radius: 3px; border-radius: 3px;
background-color: #fff; background-color: #fff;
} }
th { th {
background-color: #42b983; background-color: #42b983;
color: rgba(255,255,255,0.66); color: rgba(255, 255, 255, 0.66);
cursor: pointer; cursor: pointer;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
} }
td { td {
background-color: #f9f9f9; background-color: #f9f9f9;
} }
th, td { th,
min-width: 120px; td {
padding: 10px 20px; min-width: 120px;
} padding: 10px 20px;
}
th.active { th.active {
color: #fff; color: #fff;
} }
th.active .arrow { th.active .arrow {
opacity: 1; opacity: 1;
} }
.arrow { .arrow {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
width: 0; width: 0;
height: 0; height: 0;
margin-left: 5px; margin-left: 5px;
opacity: 0.66; opacity: 0.66;
} }
.arrow.asc { .arrow.asc {
border-left: 4px solid transparent; border-left: 4px solid transparent;
border-right: 4px solid transparent; border-right: 4px solid transparent;
border-bottom: 4px solid #fff; border-bottom: 4px solid #fff;
} }
.arrow.dsc { .arrow.dsc {
border-left: 4px solid transparent; border-left: 4px solid transparent;
border-right: 4px solid transparent; border-right: 4px solid transparent;
border-top: 4px solid #fff; border-top: 4px solid #fff;
} }
</style> </style>

View File

@ -8,55 +8,58 @@
</div> </div>
<script> <script>
Vue.createApp({ Vue.createApp({
data: () => ({ data: () => ({
input: '# hello' input: '# hello',
}), }),
computed: { computed: {
compiledMarkdown() { compiledMarkdown() {
return marked.marked(this.input, { sanitize: true }) return marked.marked(this.input, { sanitize: true })
} },
}, },
methods: { methods: {
update: _.debounce(function (e) { update: _.debounce(function (e) {
this.input = e.target.value this.input = e.target.value
}, 50) }, 50),
} },
}).mount('#editor') }).mount('#editor')
</script> </script>
<style> <style>
html, body, #editor { html,
margin: 0; body,
height: 100%; #editor {
font-family: 'Helvetica Neue', Arial, sans-serif; margin: 0;
color: #333; height: 100%;
} font-family: 'Helvetica Neue', Arial, sans-serif;
color: #333;
}
textarea, #editor div { textarea,
display: inline-block; #editor div {
overflow: auto; display: inline-block;
width: 50%; overflow: auto;
height: 100%; width: 50%;
vertical-align: top; height: 100%;
-webkit-box-sizing: border-box; vertical-align: top;
-moz-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; -moz-box-sizing: border-box;
padding: 0 20px; box-sizing: border-box;
} padding: 0 20px;
}
textarea { textarea {
border: none; border: none;
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
resize: none; resize: none;
outline: none; outline: none;
background-color: #f6f6f6; background-color: #f6f6f6;
font-size: 14px; font-size: 14px;
font-family: 'Monaco', courier, monospace; font-family: 'Monaco', courier, monospace;
padding: 20px; padding: 20px;
} }
code { code {
color: #f66; color: #f66;
} }
</style> </style>

View File

@ -1,37 +1,33 @@
<script src="../../dist/vue.global.js"></script> <script src="../../dist/vue.global.js"></script>
<script> <script>
// math helper... // math helper...
function valueToPoint (value, index, total) { function valueToPoint(value, index, total) {
var x = 0 var x = 0
var y = -value * 0.8 var y = -value * 0.8
var angle = Math.PI * 2 / total * index var angle = ((Math.PI * 2) / total) * index
var cos = Math.cos(angle) var cos = Math.cos(angle)
var sin = Math.sin(angle) var sin = Math.sin(angle)
var tx = x * cos - y * sin + 100 var tx = x * cos - y * sin + 100
var ty = x * sin + y * cos + 100 var ty = x * sin + y * cos + 100
return { return {
x: tx, x: tx,
y: ty y: ty,
}
}
const AxisLabel = {
template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
props: {
stat: Object,
index: Number,
total: Number
},
computed: {
point: function () {
return valueToPoint(
+this.stat.value + 10,
this.index,
this.total
)
} }
} }
}
const AxisLabel = {
template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
props: {
stat: Object,
index: Number,
total: Number,
},
computed: {
point: function () {
return valueToPoint(+this.stat.value + 10, this.index, this.total)
},
},
}
</script> </script>
<!-- template for the polygraph component. --> <!-- template for the polygraph component. -->
@ -49,23 +45,25 @@ const AxisLabel = {
</script> </script>
<script> <script>
const Polygraph = { const Polygraph = {
props: ['stats'], props: ['stats'],
template: '#polygraph-template', template: '#polygraph-template',
computed: { computed: {
// a computed property for the polygon's points // a computed property for the polygon's points
points() { points() {
const total = this.stats.length const total = this.stats.length
return this.stats.map((stat, i) => { return this.stats
const point = valueToPoint(stat.value, i, total) .map((stat, i) => {
return point.x + ',' + point.y const point = valueToPoint(stat.value, i, total)
}).join(' ') return point.x + ',' + point.y
} })
}, .join(' ')
components: { },
AxisLabel },
components: {
AxisLabel,
},
} }
}
</script> </script>
<!-- demo root element --> <!-- demo root element -->
@ -77,86 +75,92 @@ const Polygraph = {
<!-- controls --> <!-- controls -->
<div v-for="stat in stats"> <div v-for="stat in stats">
<label>{{stat.label}}</label> <label>{{stat.label}}</label>
<input type="range" v-model="stat.value" min="0" max="100"> <input type="range" v-model="stat.value" min="0" max="100" />
<span>{{stat.value}}</span> <span>{{stat.value}}</span>
<button @click="remove(stat)" class="remove">X</button> <button @click="remove(stat)" class="remove">X</button>
</div> </div>
<form id="add"> <form id="add">
<input name="newlabel" v-model="newLabel"> <input name="newlabel" v-model="newLabel" />
<button @click="add">Add a Stat</button> <button @click="add">Add a Stat</button>
</form> </form>
<pre id="raw">{{ stats }}</pre> <pre id="raw">{{ stats }}</pre>
</div> </div>
<script> <script>
const globalStats = [ const globalStats = [
{ label: 'A', value: 100 }, { label: 'A', value: 100 },
{ label: 'B', value: 100 }, { label: 'B', value: 100 },
{ label: 'C', value: 100 }, { label: 'C', value: 100 },
{ label: 'D', value: 100 }, { label: 'D', value: 100 },
{ label: 'E', value: 100 }, { label: 'E', value: 100 },
{ label: 'F', value: 100 } { label: 'F', value: 100 },
] ]
Vue.createApp({ Vue.createApp({
components: { components: {
Polygraph Polygraph,
},
data: () => ({
newLabel: '',
stats: globalStats
}),
methods: {
add(e) {
e.preventDefault()
if (!this.newLabel) return
this.stats.push({
label: this.newLabel,
value: 100
})
this.newLabel = ''
}, },
remove(stat) { data: () => ({
if (this.stats.length > 3) { newLabel: '',
this.stats.splice(this.stats.indexOf(stat), 1) stats: globalStats,
} else { }),
alert('Can\'t delete more!') methods: {
} add(e) {
} e.preventDefault()
} if (!this.newLabel) return
}).mount('#demo') this.stats.push({
label: this.newLabel,
value: 100,
})
this.newLabel = ''
},
remove(stat) {
if (this.stats.length > 3) {
this.stats.splice(this.stats.indexOf(stat), 1)
} else {
alert("Can't delete more!")
}
},
},
}).mount('#demo')
</script> </script>
<style> <style>
body { body {
font-family: Helvetica Neue, Arial, sans-serif; font-family:
} Helvetica Neue,
Arial,
sans-serif;
}
polygon { polygon {
fill: #42b983; fill: #42b983;
opacity: .75; opacity: 0.75;
} }
circle { circle {
fill: transparent; fill: transparent;
stroke: #999; stroke: #999;
} }
text { text {
font-family: Helvetica Neue, Arial, sans-serif; font-family:
Helvetica Neue,
Arial,
sans-serif;
font-size: 10px; font-size: 10px;
fill: #666; fill: #666;
} }
label { label {
display: inline-block; display: inline-block;
margin-left: 10px; margin-left: 10px;
width: 20px; width: 20px;
} }
#raw { #raw {
position: absolute; position: absolute;
top: 0; top: 0;
left: 300px; left: 300px;
} }
</style> </style>

View File

@ -1,49 +1,81 @@
<script src="../../dist/vue.global.js"></script> <script src="../../dist/vue.global.js"></script>
<link rel="stylesheet" href="../../../../node_modules/todomvc-app-css/index.css"> <link
rel="stylesheet"
href="../../../../node_modules/todomvc-app-css/index.css"
/>
<div id="app"> <div id="app">
<section class="todoapp"> <section class="todoapp">
<header class="header"> <header class="header">
<h1>todos</h1> <h1>todos</h1>
<input class="new-todo" <input
autofocus autocomplete="off" class="new-todo"
placeholder="What needs to be done?" autofocus
v-model="newTodo" autocomplete="off"
@keyup.enter="addTodo"> placeholder="What needs to be done?"
v-model="newTodo"
@keyup.enter="addTodo"
/>
</header> </header>
<section class="main" v-show="todos.length"> <section class="main" v-show="todos.length">
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="allDone"> <input
id="toggle-all"
class="toggle-all"
type="checkbox"
v-model="allDone"
/>
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul class="todo-list"> <ul class="todo-list">
<li v-for="todo in filteredTodos" <li
class="todo" v-for="todo in filteredTodos"
:key="todo.id" class="todo"
:class="{ completed: todo.completed, editing: todo === editedTodo }"> :key="todo.id"
:class="{ completed: todo.completed, editing: todo === editedTodo }"
>
<div class="view"> <div class="view">
<input class="toggle" type="checkbox" v-model="todo.completed"> <input class="toggle" type="checkbox" v-model="todo.completed" />
<label @dblclick="editTodo(todo)">{{ todo.title }}</label> <label @dblclick="editTodo(todo)">{{ todo.title }}</label>
<button class="destroy" @click="removeTodo(todo)"></button> <button class="destroy" @click="removeTodo(todo)"></button>
</div> </div>
<input class="edit" type="text" <input
v-model="todo.title" class="edit"
v-todo-focus="todo === editedTodo" type="text"
@blur="doneEdit(todo)" v-model="todo.title"
@keyup.enter="doneEdit(todo)" v-todo-focus="todo === editedTodo"
@keyup.escape="cancelEdit(todo)" @blur="doneEdit(todo)"
> @keyup.enter="doneEdit(todo)"
@keyup.escape="cancelEdit(todo)"
/>
</li> </li>
</ul> </ul>
</section> </section>
<footer class="footer" v-show="todos.length"> <footer class="footer" v-show="todos.length">
<span class="todo-count"> <span class="todo-count">
<strong>{{ remaining }}</strong> <span>{{ pluralize(remaining) }} left</span> <strong>{{ remaining }}</strong>
</span> <span>{{ pluralize(remaining) }} left</span>
</span>
<ul class="filters"> <ul class="filters">
<li><a href="#/all" :class="{ selected: visibility === 'all' }">All</a></li> <li>
<li><a href="#/active" :class="{ selected: visibility === 'active' }">Active</a></li> <a href="#/all" :class="{ selected: visibility === 'all' }">All</a>
<li><a href="#/completed" :class="{ selected: visibility === 'completed' }">Completed</a></li> </li>
<li>
<a href="#/active" :class="{ selected: visibility === 'active' }"
>Active</a
>
</li>
<li>
<a
href="#/completed"
:class="{ selected: visibility === 'completed' }"
>Completed</a
>
</li>
</ul> </ul>
<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining"> <button
class="clear-completed"
@click="removeCompleted"
v-show="todos.length > remaining"
>
Clear completed Clear completed
</button> </button>
</footer> </footer>
@ -51,146 +83,146 @@
</div> </div>
<script> <script>
const STORAGE_KEY = 'todos-vuejs-3.x' const STORAGE_KEY = 'todos-vuejs-3.x'
const todoStorage = { const todoStorage = {
fetch() { fetch() {
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
todos.forEach((todo, index) => { todos.forEach((todo, index) => {
todo.id = index todo.id = index
})
todoStorage.uid = todos.length
return todos
},
save(todos) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
}
}
const filters = {
all(todos) {
return todos
},
active(todos) {
return todos.filter((todo) => {
return !todo.completed
})
},
completed(todos) {
return todos.filter(function (todo) {
return todo.completed
})
}
}
Vue.createApp({
// app initial state
data: () => ({
todos: todoStorage.fetch(),
newTodo: '',
editedTodo: null,
visibility: 'all'
}),
// watch todos change for localStorage persistence
watch: {
todos: {
handler(todos) {
todoStorage.save(todos)
},
deep: true
}
},
mounted() {
window.addEventListener('hashchange', this.onHashChange)
this.onHashChange()
},
computed: {
filteredTodos() {
return filters[this.visibility](this.todos)
},
remaining() {
return filters.active(this.todos).length
},
allDone: {
get() {
return this.remaining === 0
},
set(value) {
this.todos.forEach(function (todo) {
todo.completed = value
})
}
}
},
// methods that implement data logic.
// note there's no DOM manipulation here at all.
methods: {
addTodo() {
var value = this.newTodo && this.newTodo.trim()
if (!value) {
return
}
this.todos.push({
id: todoStorage.uid++,
title: value,
completed: false
}) })
this.newTodo = '' todoStorage.uid = todos.length
return todos
}, },
save(todos) {
removeTodo(todo) { localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
this.todos.splice(this.todos.indexOf(todo), 1)
}, },
editTodo(todo) {
this.beforeEditCache = todo.title
this.editedTodo = todo
},
doneEdit(todo) {
if (!this.editedTodo) {
return
}
this.editedTodo = null
todo.title = todo.title.trim()
if (!todo.title) {
this.removeTodo(todo)
}
},
cancelEdit(todo) {
this.editedTodo = null
todo.title = this.beforeEditCache
},
removeCompleted() {
this.todos = filters.active(this.todos)
},
onHashChange() {
var visibility = window.location.hash.replace(/#\/?/, '')
if (filters[visibility]) {
this.visibility = visibility
} else {
window.location.hash = ''
this.visibility = 'all'
}
},
pluralize (n) {
return n === 1 ? 'item' : 'items'
}
},
directives: {
'todo-focus'(el, binding) {
if (binding.value) {
el.focus()
}
}
} }
}).mount('#app')
const filters = {
all(todos) {
return todos
},
active(todos) {
return todos.filter(todo => {
return !todo.completed
})
},
completed(todos) {
return todos.filter(function (todo) {
return todo.completed
})
},
}
Vue.createApp({
// app initial state
data: () => ({
todos: todoStorage.fetch(),
newTodo: '',
editedTodo: null,
visibility: 'all',
}),
// watch todos change for localStorage persistence
watch: {
todos: {
handler(todos) {
todoStorage.save(todos)
},
deep: true,
},
},
mounted() {
window.addEventListener('hashchange', this.onHashChange)
this.onHashChange()
},
computed: {
filteredTodos() {
return filters[this.visibility](this.todos)
},
remaining() {
return filters.active(this.todos).length
},
allDone: {
get() {
return this.remaining === 0
},
set(value) {
this.todos.forEach(function (todo) {
todo.completed = value
})
},
},
},
// methods that implement data logic.
// note there's no DOM manipulation here at all.
methods: {
addTodo() {
var value = this.newTodo && this.newTodo.trim()
if (!value) {
return
}
this.todos.push({
id: todoStorage.uid++,
title: value,
completed: false,
})
this.newTodo = ''
},
removeTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
},
editTodo(todo) {
this.beforeEditCache = todo.title
this.editedTodo = todo
},
doneEdit(todo) {
if (!this.editedTodo) {
return
}
this.editedTodo = null
todo.title = todo.title.trim()
if (!todo.title) {
this.removeTodo(todo)
}
},
cancelEdit(todo) {
this.editedTodo = null
todo.title = this.beforeEditCache
},
removeCompleted() {
this.todos = filters.active(this.todos)
},
onHashChange() {
var visibility = window.location.hash.replace(/#\/?/, '')
if (filters[visibility]) {
this.visibility = visibility
} else {
window.location.hash = ''
this.visibility = 'all'
}
},
pluralize(n) {
return n === 1 ? 'item' : 'items'
},
},
directives: {
'todo-focus'(el, binding) {
if (binding.value) {
el.focus()
}
},
},
}).mount('#app')
</script> </script>

View File

@ -22,43 +22,42 @@
</script> </script>
<!-- item script --> <!-- item script -->
<script> <script>
const TreeItem = { const TreeItem = {
name: 'TreeItem', // necessary for self-reference name: 'TreeItem', // necessary for self-reference
template: '#item-template', template: '#item-template',
props: { props: {
model: Object model: Object,
}, },
data() { data() {
return { return {
open: false open: false,
}
},
computed: {
isFolder() {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle() {
if (this.isFolder) {
this.open = !this.open
} }
}, },
changeType() { computed: {
if (!this.isFolder) { isFolder() {
this.model.children = [] return this.model.children && this.model.children.length
this.addChild() },
this.open = true },
} methods: {
toggle() {
if (this.isFolder) {
this.open = !this.open
}
},
changeType() {
if (!this.isFolder) {
this.model.children = []
this.addChild()
this.open = true
}
},
addChild() {
this.model.children.push({
name: 'new stuff',
})
},
}, },
addChild() {
this.model.children.push({
name: 'new stuff'
})
}
} }
}
</script> </script>
<p>(You can double click on an item to turn it into a folder.)</p> <p>(You can double click on an item to turn it into a folder.)</p>
@ -69,43 +68,37 @@ const TreeItem = {
</ul> </ul>
<script> <script>
const treeData = { const treeData = {
name: 'My Tree', name: 'My Tree',
children: [ children: [
{ name: 'hello' }, { name: 'hello' },
{ name: 'wat' }, { name: 'wat' },
{ {
name: 'child folder', name: 'child folder',
children: [ children: [
{ {
name: 'child folder', name: 'child folder',
children: [ children: [{ name: 'hello' }, { name: 'wat' }],
{ name: 'hello' }, },
{ name: 'wat' } { name: 'hello' },
] { name: 'wat' },
}, {
{ name: 'hello' }, name: 'child folder',
{ name: 'wat' }, children: [{ name: 'hello' }, { name: 'wat' }],
{ },
name: 'child folder', ],
children: [ },
{ name: 'hello' }, ],
{ name: 'wat' } }
]
}
]
}
]
}
Vue.createApp({ Vue.createApp({
components: { components: {
TreeItem TreeItem,
}, },
data: () => ({ data: () => ({
treeData treeData,
}) }),
}).mount('#demo') }).mount('#demo')
</script> </script>
<style> <style>

View File

@ -3,58 +3,67 @@
<div id="demo"> <div id="demo">
<h1>Latest Vue.js Commits</h1> <h1>Latest Vue.js Commits</h1>
<template v-for="branch in branches"> <template v-for="branch in branches">
<input type="radio" <input
type="radio"
:id="branch" :id="branch"
:value="branch" :value="branch"
name="branch" name="branch"
v-model="currentBranch"> v-model="currentBranch"
/>
<label :for="branch">{{ branch }}</label> <label :for="branch">{{ branch }}</label>
</template> </template>
<p>vuejs/core@{{ currentBranch }}</p> <p>vuejs/core@{{ currentBranch }}</p>
<ul> <ul>
<li v-for="{ html_url, sha, author, commit } in commits"> <li v-for="{ html_url, sha, author, commit } in commits">
<a :href="html_url" target="_blank" class="commit">{{ sha.slice(0, 7) }}</a> <a :href="html_url" target="_blank" class="commit"
- <span class="message">{{ truncate(commit.message) }}</span><br> >{{ sha.slice(0, 7) }}</a
by <span class="author"><a :href="author.html_url" target="_blank">{{ commit.author.name }}</a></span> >
- <span class="message">{{ truncate(commit.message) }}</span><br />
by
<span class="author"
><a :href="author.html_url" target="_blank"
>{{ commit.author.name }}</a
></span
>
at <span class="date">{{ formatDate(commit.author.date) }}</span> at <span class="date">{{ formatDate(commit.author.date) }}</span>
</li> </li>
</ul> </ul>
</div> </div>
<script> <script>
const { createApp, ref, watchEffect } = Vue const { createApp, ref, watchEffect } = Vue
const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=` const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=`
const truncate = v => { const truncate = v => {
const newline = v.indexOf('\n') const newline = v.indexOf('\n')
return newline > 0 ? v.slice(0, newline) : v return newline > 0 ? v.slice(0, newline) : v
}
const formatDate = v => v.replace(/T|Z/g, ' ')
createApp({
setup() {
const currentBranch = ref('main')
const commits = ref(null)
watchEffect(() => {
fetch(`${API_URL}${currentBranch.value}`)
.then(res => res.json())
.then(data => {
console.log(data)
commits.value = data
})
})
return {
branches: ['main', 'v2-compat'],
currentBranch,
commits,
truncate,
formatDate
}
} }
}).mount('#demo')
const formatDate = v => v.replace(/T|Z/g, ' ')
createApp({
setup() {
const currentBranch = ref('main')
const commits = ref(null)
watchEffect(() => {
fetch(`${API_URL}${currentBranch.value}`)
.then(res => res.json())
.then(data => {
console.log(data)
commits.value = data
})
})
return {
branches: ['main', 'v2-compat'],
currentBranch,
commits,
truncate,
formatDate,
}
},
}).mount('#demo')
</script> </script>
<style> <style>
@ -69,7 +78,8 @@ createApp({
line-height: 1.5em; line-height: 1.5em;
margin-bottom: 20px; margin-bottom: 20px;
} }
.author, .date { .author,
.date {
font-weight: bold; font-weight: bold;
} }
</style> </style>

View File

@ -26,148 +26,147 @@
</script> </script>
<!-- DemoGrid component script --> <!-- DemoGrid component script -->
<script> <script>
const { reactive, computed } = Vue const { reactive, computed } = Vue
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1) const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
const DemoGrid = { const DemoGrid = {
template: '#grid-template', template: '#grid-template',
props: { props: {
data: Array, data: Array,
columns: Array, columns: Array,
filterKey: String filterKey: String,
}, },
setup(props) { setup(props) {
const state = reactive({ const state = reactive({
sortKey: '', sortKey: '',
sortOrders: props.columns.reduce((o, key) => (o[key] = 1, o), {}) sortOrders: props.columns.reduce((o, key) => ((o[key] = 1), o), {}),
}) })
const filteredData = computed(() => { const filteredData = computed(() => {
let { data, filterKey } = props let { data, filterKey } = props
if (filterKey) { if (filterKey) {
filterKey = filterKey.toLowerCase() filterKey = filterKey.toLowerCase()
data = data.filter(row => { data = data.filter(row => {
return Object.keys(row).some(key => { return Object.keys(row).some(key => {
return String(row[key]).toLowerCase().indexOf(filterKey) > -1 return String(row[key]).toLowerCase().indexOf(filterKey) > -1
})
}) })
}) }
} const { sortKey } = state
const { sortKey } = state if (sortKey) {
if (sortKey) { const order = state.sortOrders[sortKey]
const order = state.sortOrders[sortKey] data = data.slice().sort((a, b) => {
data = data.slice().sort((a, b) => { a = a[sortKey]
a = a[sortKey] b = b[sortKey]
b = b[sortKey] return (a === b ? 0 : a > b ? 1 : -1) * order
return (a === b ? 0 : a > b ? 1 : -1) * order })
}) }
} return data
return data })
})
function sortBy(key) { function sortBy(key) {
state.sortKey = key state.sortKey = key
state.sortOrders[key] *= -1 state.sortOrders[key] *= -1
} }
return { return {
state, state,
filteredData, filteredData,
sortBy, sortBy,
capitalize capitalize,
} }
},
} }
}
</script> </script>
<!-- App template (in DOM) --> <!-- App template (in DOM) -->
<div id="demo"> <div id="demo">
<form id="search"> <form id="search">Search <input name="query" v-model="searchQuery" /></form>
Search <input name="query" v-model="searchQuery"> <demo-grid :data="gridData" :columns="gridColumns" :filter-key="searchQuery">
</form>
<demo-grid
:data="gridData"
:columns="gridColumns"
:filter-key="searchQuery">
</demo-grid> </demo-grid>
</div> </div>
<!-- App script --> <!-- App script -->
<script> <script>
Vue.createApp({ Vue.createApp({
components: { components: {
DemoGrid DemoGrid,
}, },
data: () => ({ data: () => ({
searchQuery: '', searchQuery: '',
gridColumns: ['name', 'power'], gridColumns: ['name', 'power'],
gridData: [ gridData: [
{ name: 'Chuck Norris', power: Infinity }, { name: 'Chuck Norris', power: Infinity },
{ name: 'Bruce Lee', power: 9000 }, { name: 'Bruce Lee', power: 9000 },
{ name: 'Jackie Chan', power: 7000 }, { name: 'Jackie Chan', power: 7000 },
{ name: 'Jet Li', power: 8000 } { name: 'Jet Li', power: 8000 },
] ],
}) }),
}).mount('#demo') }).mount('#demo')
</script> </script>
<style> <style>
body { body {
font-family: Helvetica Neue, Arial, sans-serif; font-family:
font-size: 14px; Helvetica Neue,
color: #444; Arial,
} sans-serif;
font-size: 14px;
color: #444;
}
table { table {
border: 2px solid #42b983; border: 2px solid #42b983;
border-radius: 3px; border-radius: 3px;
background-color: #fff; background-color: #fff;
} }
th { th {
background-color: #42b983; background-color: #42b983;
color: rgba(255,255,255,0.66); color: rgba(255, 255, 255, 0.66);
cursor: pointer; cursor: pointer;
-webkit-user-select: none; -webkit-user-select: none;
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
} }
td { td {
background-color: #f9f9f9; background-color: #f9f9f9;
} }
th, td { th,
min-width: 120px; td {
padding: 10px 20px; min-width: 120px;
} padding: 10px 20px;
}
th.active { th.active {
color: #fff; color: #fff;
} }
th.active .arrow { th.active .arrow {
opacity: 1; opacity: 1;
} }
.arrow { .arrow {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
width: 0; width: 0;
height: 0; height: 0;
margin-left: 5px; margin-left: 5px;
opacity: 0.66; opacity: 0.66;
} }
.arrow.asc { .arrow.asc {
border-left: 4px solid transparent; border-left: 4px solid transparent;
border-right: 4px solid transparent; border-right: 4px solid transparent;
border-bottom: 4px solid #fff; border-bottom: 4px solid #fff;
} }
.arrow.dsc { .arrow.dsc {
border-left: 4px solid transparent; border-left: 4px solid transparent;
border-right: 4px solid transparent; border-right: 4px solid transparent;
border-top: 4px solid #fff; border-top: 4px solid #fff;
} }
</style> </style>

View File

@ -8,55 +8,62 @@
</div> </div>
<script> <script>
const { ref, computed } = Vue const { ref, computed } = Vue
Vue.createApp({ Vue.createApp({
setup() { setup() {
const input = ref('# hello') const input = ref('# hello')
const output = computed(() => marked.marked(input.value, { sanitize: true })) const output = computed(() =>
const update = _.debounce(e => { input.value = e.target.value }, 50) marked.marked(input.value, { sanitize: true }),
)
const update = _.debounce(e => {
input.value = e.target.value
}, 50)
return { return {
input, input,
output, output,
update update,
} }
} },
}).mount('#editor') }).mount('#editor')
</script> </script>
<style> <style>
html, body, #editor { html,
margin: 0; body,
height: 100%; #editor {
font-family: 'Helvetica Neue', Arial, sans-serif; margin: 0;
color: #333; height: 100%;
} font-family: 'Helvetica Neue', Arial, sans-serif;
color: #333;
}
textarea, #editor div { textarea,
display: inline-block; #editor div {
overflow: auto; display: inline-block;
width: 50%; overflow: auto;
height: 100%; width: 50%;
vertical-align: top; height: 100%;
-webkit-box-sizing: border-box; vertical-align: top;
-moz-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box; -moz-box-sizing: border-box;
padding: 0 20px; box-sizing: border-box;
} padding: 0 20px;
}
textarea { textarea {
border: none; border: none;
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
resize: none; resize: none;
outline: none; outline: none;
background-color: #f6f6f6; background-color: #f6f6f6;
font-size: 14px; font-size: 14px;
font-family: 'Monaco', courier, monospace; font-family: 'Monaco', courier, monospace;
padding: 20px; padding: 20px;
} }
code { code {
color: #f66; color: #f66;
} }
</style> </style>

View File

@ -1,39 +1,37 @@
<script src="../../dist/vue.global.js"></script> <script src="../../dist/vue.global.js"></script>
<script> <script>
const { ref, reactive, computed, createApp } = Vue const { ref, reactive, computed, createApp } = Vue
// math helper... // math helper...
function valueToPoint (value, index, total) { function valueToPoint(value, index, total) {
var x = 0 var x = 0
var y = -value * 0.8 var y = -value * 0.8
var angle = Math.PI * 2 / total * index var angle = ((Math.PI * 2) / total) * index
var cos = Math.cos(angle) var cos = Math.cos(angle)
var sin = Math.sin(angle) var sin = Math.sin(angle)
var tx = x * cos - y * sin + 100 var tx = x * cos - y * sin + 100
var ty = x * sin + y * cos + 100 var ty = x * sin + y * cos + 100
return {
x: tx,
y: ty
}
}
const AxisLabel = {
template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
props: {
stat: Object,
index: Number,
total: Number
},
setup(props) {
return { return {
point: computed(() => valueToPoint( x: tx,
+props.stat.value + 10, y: ty,
props.index,
props.total
))
} }
} }
}
const AxisLabel = {
template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
props: {
stat: Object,
index: Number,
total: Number,
},
setup(props) {
return {
point: computed(() =>
valueToPoint(+props.stat.value + 10, props.index, props.total),
),
}
},
}
</script> </script>
<!-- template for the polygraph component. --> <!-- template for the polygraph component. -->
@ -51,24 +49,26 @@ const AxisLabel = {
</script> </script>
<script> <script>
const Polygraph = { const Polygraph = {
props: ['stats'], props: ['stats'],
template: '#polygraph-template', template: '#polygraph-template',
setup(props) { setup(props) {
return { return {
points: computed(() => { points: computed(() => {
const total = props.stats.length const total = props.stats.length
return props.stats.map((stat, i) => { return props.stats
const point = valueToPoint(stat.value, i, total) .map((stat, i) => {
return point.x + ',' + point.y const point = valueToPoint(stat.value, i, total)
}).join(' ') return point.x + ',' + point.y
}) })
} .join(' ')
}, }),
components: { }
AxisLabel },
components: {
AxisLabel,
},
} }
}
</script> </script>
<!-- demo root element --> <!-- demo root element -->
@ -80,93 +80,99 @@ const Polygraph = {
<!-- controls --> <!-- controls -->
<div v-for="stat in stats"> <div v-for="stat in stats">
<label>{{stat.label}}</label> <label>{{stat.label}}</label>
<input type="range" v-model="stat.value" min="0" max="100"> <input type="range" v-model="stat.value" min="0" max="100" />
<span>{{stat.value}}</span> <span>{{stat.value}}</span>
<button @click="remove(stat)" class="remove">X</button> <button @click="remove(stat)" class="remove">X</button>
</div> </div>
<form id="add"> <form id="add">
<input name="newlabel" v-model="newLabel"> <input name="newlabel" v-model="newLabel" />
<button @click="add">Add a Stat</button> <button @click="add">Add a Stat</button>
</form> </form>
<pre id="raw">{{ stats }}</pre> <pre id="raw">{{ stats }}</pre>
</div> </div>
<script> <script>
const globalStats = [ const globalStats = [
{ label: 'A', value: 100 }, { label: 'A', value: 100 },
{ label: 'B', value: 100 }, { label: 'B', value: 100 },
{ label: 'C', value: 100 }, { label: 'C', value: 100 },
{ label: 'D', value: 100 }, { label: 'D', value: 100 },
{ label: 'E', value: 100 }, { label: 'E', value: 100 },
{ label: 'F', value: 100 } { label: 'F', value: 100 },
] ]
createApp({ createApp({
components: { components: {
Polygraph Polygraph,
}, },
setup() { setup() {
const newLabel = ref('') const newLabel = ref('')
const stats = reactive(globalStats) const stats = reactive(globalStats)
function add(e) { function add(e) {
e.preventDefault() e.preventDefault()
if (!newLabel.value) return if (!newLabel.value) return
stats.push({ stats.push({
label: newLabel.value, label: newLabel.value,
value: 100 value: 100,
}) })
newLabel.value = '' newLabel.value = ''
}
function remove(stat) {
if (stats.length > 3) {
stats.splice(stats.indexOf(stat), 1)
} else {
alert('Can\'t delete more!')
} }
}
return { function remove(stat) {
newLabel, if (stats.length > 3) {
stats, stats.splice(stats.indexOf(stat), 1)
add, } else {
remove alert("Can't delete more!")
} }
} }
}).mount('#demo')
return {
newLabel,
stats,
add,
remove,
}
},
}).mount('#demo')
</script> </script>
<style> <style>
body { body {
font-family: Helvetica Neue, Arial, sans-serif; font-family:
} Helvetica Neue,
Arial,
sans-serif;
}
polygon { polygon {
fill: #42b983; fill: #42b983;
opacity: .75; opacity: 0.75;
} }
circle { circle {
fill: transparent; fill: transparent;
stroke: #999; stroke: #999;
} }
text { text {
font-family: Helvetica Neue, Arial, sans-serif; font-family:
Helvetica Neue,
Arial,
sans-serif;
font-size: 10px; font-size: 10px;
fill: #666; fill: #666;
} }
label { label {
display: inline-block; display: inline-block;
margin-left: 10px; margin-left: 10px;
width: 20px; width: 20px;
} }
#raw { #raw {
position: absolute; position: absolute;
top: 0; top: 0;
left: 300px; left: 300px;
} }
</style> </style>

View File

@ -1,51 +1,86 @@
<script src="../../dist/vue.global.js"></script> <script src="../../dist/vue.global.js"></script>
<link rel="stylesheet" href="../../../../node_modules/todomvc-app-css/index.css"> <link
rel="stylesheet"
href="../../../../node_modules/todomvc-app-css/index.css"
/>
<div id="app"> <div id="app">
<section class="todoapp"> <section class="todoapp">
<header class="header"> <header class="header">
<h1>todos</h1> <h1>todos</h1>
<input class="new-todo" <input
autofocus autocomplete="off" class="new-todo"
placeholder="What needs to be done?" autofocus
v-model="state.newTodo" autocomplete="off"
@keyup.enter="addTodo"> placeholder="What needs to be done?"
v-model="state.newTodo"
@keyup.enter="addTodo"
/>
</header> </header>
<section class="main" v-show="state.todos.length"> <section class="main" v-show="state.todos.length">
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="state.allDone"> <input
id="toggle-all"
class="toggle-all"
type="checkbox"
v-model="state.allDone"
/>
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul class="todo-list"> <ul class="todo-list">
<li v-for="todo in state.filteredTodos" <li
class="todo" v-for="todo in state.filteredTodos"
:key="todo.id" class="todo"
:class="{ completed: todo.completed, editing: todo === state.editedTodo }"> :key="todo.id"
:class="{ completed: todo.completed, editing: todo === state.editedTodo }"
>
<div class="view"> <div class="view">
<input class="toggle" type="checkbox" v-model="todo.completed"> <input class="toggle" type="checkbox" v-model="todo.completed" />
<label @dblclick="editTodo(todo)">{{ todo.title }}</label> <label @dblclick="editTodo(todo)">{{ todo.title }}</label>
<button class="destroy" @click="removeTodo(todo)"></button> <button class="destroy" @click="removeTodo(todo)"></button>
</div> </div>
<input class="edit" type="text" <input
v-model="todo.title" class="edit"
v-todo-focus="todo === state.editedTodo" type="text"
@blur="doneEdit(todo)" v-model="todo.title"
@keyup.enter="doneEdit(todo)" v-todo-focus="todo === state.editedTodo"
@keyup.escape="cancelEdit(todo)" @blur="doneEdit(todo)"
> @keyup.enter="doneEdit(todo)"
@keyup.escape="cancelEdit(todo)"
/>
</li> </li>
</ul> </ul>
</section> </section>
<footer class="footer" v-show="state.todos.length"> <footer class="footer" v-show="state.todos.length">
<span class="todo-count"> <span class="todo-count">
<strong>{{ state.remaining }}</strong> <strong>{{ state.remaining }}</strong>
<span>{{ state.remainingText }}</span> <span>{{ state.remainingText }}</span>
</span> </span>
<ul class="filters"> <ul class="filters">
<li><a href="#/all" :class="{ selected: state.visibility === 'all' }">All</a></li> <li>
<li><a href="#/active" :class="{ selected: state.visibility === 'active' }">Active</a></li> <a href="#/all" :class="{ selected: state.visibility === 'all' }"
<li><a href="#/completed" :class="{ selected: state.visibility === 'completed' }">Completed</a></li> >All</a
>
</li>
<li>
<a
href="#/active"
:class="{ selected: state.visibility === 'active' }"
>Active</a
>
</li>
<li>
<a
href="#/completed"
:class="{ selected: state.visibility === 'completed' }"
>Completed</a
>
</li>
</ul> </ul>
<button class="clear-completed" @click="removeCompleted" v-show="state.todos.length > state.remaining"> <button
class="clear-completed"
@click="removeCompleted"
v-show="state.todos.length > state.remaining"
>
Clear completed Clear completed
</button> </button>
</footer> </footer>
@ -53,154 +88,155 @@
</div> </div>
<script> <script>
const { createApp, reactive, computed, watchEffect, onMounted, onUnmounted } = Vue const { createApp, reactive, computed, watchEffect, onMounted, onUnmounted } =
Vue
const STORAGE_KEY = 'todos-vuejs-3.x' const STORAGE_KEY = 'todos-vuejs-3.x'
const todoStorage = { const todoStorage = {
fetch () { fetch() {
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]') const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
todos.forEach((todo, index) => { todos.forEach((todo, index) => {
todo.id = index todo.id = index
}) })
todoStorage.uid = todos.length todoStorage.uid = todos.length
return todos return todos
}, },
save (todos) { save(todos) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)) localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
},
} }
}
const filters = { const filters = {
all (todos) { all(todos) {
return todos return todos
}, },
active (todos) { active(todos) {
return todos.filter((todo) => { return todos.filter(todo => {
return !todo.completed return !todo.completed
}) })
}, },
completed (todos) { completed(todos) {
return todos.filter(function (todo) { return todos.filter(function (todo) {
return todo.completed return todo.completed
}) })
},
} }
}
function pluralize (n) { function pluralize(n) {
return n === 1 ? 'item' : 'items' return n === 1 ? 'item' : 'items'
} }
createApp({ createApp({
setup () { setup() {
const state = reactive({ const state = reactive({
todos: todoStorage.fetch(), todos: todoStorage.fetch(),
editedTodo: null, editedTodo: null,
newTodo: '', newTodo: '',
beforeEditCache: '', beforeEditCache: '',
visibility: 'all', visibility: 'all',
remaining: computed(() => { remaining: computed(() => {
return filters.active(state.todos).length return filters.active(state.todos).length
}), }),
remainingText: computed(() => { remainingText: computed(() => {
return ` ${pluralize(state.remaining)} left` return ` ${pluralize(state.remaining)} left`
}), }),
filteredTodos: computed(() => { filteredTodos: computed(() => {
return filters[state.visibility](state.todos) return filters[state.visibility](state.todos)
}), }),
allDone: computed({ allDone: computed({
get: function () { get: function () {
return state.remaining === 0 return state.remaining === 0
}, },
set: function (value) { set: function (value) {
state.todos.forEach((todo) => { state.todos.forEach(todo => {
todo.completed = value todo.completed = value
}) })
},
}),
})
watchEffect(() => {
todoStorage.save(state.todos)
})
onMounted(() => {
window.addEventListener('hashchange', onHashChange)
onHashChange()
})
onUnmounted(() => {
window.removeEventListener('hashchange', onHashChange)
})
function onHashChange() {
const visibility = window.location.hash.replace(/#\/?/, '')
if (filters[visibility]) {
state.visibility = visibility
} else {
window.location.hash = ''
state.visibility = 'all'
} }
})
})
watchEffect(() => {
todoStorage.save(state.todos)
})
onMounted(() => {
window.addEventListener('hashchange', onHashChange)
onHashChange()
})
onUnmounted(() => {
window.removeEventListener('hashchange', onHashChange)
})
function onHashChange () {
const visibility = window.location.hash.replace(/#\/?/, '')
if (filters[visibility]) {
state.visibility = visibility
} else {
window.location.hash = ''
state.visibility = 'all'
} }
}
function addTodo () { function addTodo() {
const value = state.newTodo && state.newTodo.trim() const value = state.newTodo && state.newTodo.trim()
if (!value) { if (!value) {
return return
}
state.todos.push({
id: todoStorage.uid++,
title: value,
completed: false,
})
state.newTodo = ''
} }
state.todos.push({
id: todoStorage.uid++,
title: value,
completed: false
})
state.newTodo = ''
}
function removeTodo (todo) { function removeTodo(todo) {
state.todos.splice(state.todos.indexOf(todo), 1) state.todos.splice(state.todos.indexOf(todo), 1)
}
function editTodo (todo) {
state.beforeEditCache = todo.title
state.editedTodo = todo
}
function doneEdit (todo) {
if (!state.editedTodo) {
return
} }
state.editedTodo = null
todo.title = todo.title.trim() function editTodo(todo) {
if (!todo.title) { state.beforeEditCache = todo.title
removeTodo(todo) state.editedTodo = todo
} }
}
function cancelEdit (todo) { function doneEdit(todo) {
state.editedTodo = null if (!state.editedTodo) {
todo.title = state.beforeEditCache return
} }
state.editedTodo = null
function removeCompleted () { todo.title = todo.title.trim()
state.todos = filters.active(state.todos) if (!todo.title) {
} removeTodo(todo)
}
return {
state,
addTodo,
removeTodo,
editTodo,
doneEdit,
cancelEdit,
removeCompleted
}
},
directives: {
'todo-focus': (el, { value }) => {
if (value) {
el.focus()
} }
}
} function cancelEdit(todo) {
}).mount('#app') state.editedTodo = null
todo.title = state.beforeEditCache
}
function removeCompleted() {
state.todos = filters.active(state.todos)
}
return {
state,
addTodo,
removeTodo,
editTodo,
doneEdit,
cancelEdit,
removeCompleted,
}
},
directives: {
'todo-focus': (el, { value }) => {
if (value) {
el.focus()
}
},
},
}).mount('#app')
</script> </script>

View File

@ -22,46 +22,46 @@
</script> </script>
<!-- item script --> <!-- item script -->
<script> <script>
const { reactive, computed, toRefs } = Vue const { reactive, computed, toRefs } = Vue
const TreeItem = { const TreeItem = {
name: 'TreeItem', // necessary for self-reference name: 'TreeItem', // necessary for self-reference
template: '#item-template', template: '#item-template',
props: { props: {
model: Object model: Object,
}, },
setup(props) { setup(props) {
const state = reactive({ const state = reactive({
open: false, open: false,
isFolder: computed(() => { isFolder: computed(() => {
return props.model.children && props.model.children.length return props.model.children && props.model.children.length
}),
}) })
})
function toggle() { function toggle() {
state.open = !state.open state.open = !state.open
}
function changeType() {
if (!state.isFolder) {
props.model.children = []
addChild()
state.open = true
} }
}
function addChild() { function changeType() {
props.model.children.push({ name: 'new stuff' }) if (!state.isFolder) {
} props.model.children = []
addChild()
state.open = true
}
}
return { function addChild() {
...toRefs(state), props.model.children.push({ name: 'new stuff' })
toggle, }
changeType,
addChild return {
} ...toRefs(state),
toggle,
changeType,
addChild,
}
},
} }
}
</script> </script>
<p>(You can double click on an item to turn it into a folder.)</p> <p>(You can double click on an item to turn it into a folder.)</p>
@ -72,43 +72,37 @@ const TreeItem = {
</ul> </ul>
<script> <script>
const treeData = { const treeData = {
name: 'My Tree', name: 'My Tree',
children: [ children: [
{ name: 'hello' }, { name: 'hello' },
{ name: 'wat' }, { name: 'wat' },
{ {
name: 'child folder', name: 'child folder',
children: [ children: [
{ {
name: 'child folder', name: 'child folder',
children: [ children: [{ name: 'hello' }, { name: 'wat' }],
{ name: 'hello' }, },
{ name: 'wat' } { name: 'hello' },
] { name: 'wat' },
}, {
{ name: 'hello' }, name: 'child folder',
{ name: 'wat' }, children: [{ name: 'hello' }, { name: 'wat' }],
{ },
name: 'child folder', ],
children: [ },
{ name: 'hello' }, ],
{ name: 'wat' } }
]
}
]
}
]
}
Vue.createApp({ Vue.createApp({
components: { components: {
TreeItem TreeItem,
}, },
data: () => ({ data: () => ({
treeData treeData,
}) }),
}).mount('#demo') }).mount('#demo')
</script> </script>
<style> <style>

View File

@ -6,78 +6,83 @@
<button @click="reset">reset</button> <button @click="reset">reset</button>
<button @click="shuffle">shuffle</button> <button @click="shuffle">shuffle</button>
<transition-group tag="ul" name="fade" class="container"> <transition-group tag="ul" name="fade" class="container">
<item v-for="item in items" <item
v-for="item in items"
class="item" class="item"
:msg="item" :msg="item"
:key="item" :key="item"
@rm="remove(item)"> @rm="remove(item)"
>
</item> </item>
</transition-group> </transition-group>
</div> </div>
<script> <script>
const getInitialItems = () => [1, 2, 3, 4, 5] const getInitialItems = () => [1, 2, 3, 4, 5]
let id = getInitialItems().length + 1 let id = getInitialItems().length + 1
const Item = { const Item = {
props: ['msg'], props: ['msg'],
template: `<div>{{ msg }} <button @click="$emit('rm')">x</button></div>` template: `<div>{{ msg }} <button @click="$emit('rm')">x</button></div>`,
}
Vue.createApp({
components: {
Item
},
data() {
return {
items: getInitialItems()
}
},
methods: {
insert () {
const i = Math.round(Math.random() * this.items.length)
this.items.splice(i, 0, id++)
},
reset () {
this.items = getInitialItems()
},
shuffle () {
this.items = _.shuffle(this.items)
},
remove (item) {
const i = this.items.indexOf(item)
if (i > -1) {
this.items.splice(i, 1)
}
}
} }
}).mount('#app')
Vue.createApp({
components: {
Item,
},
data() {
return {
items: getInitialItems(),
}
},
methods: {
insert() {
const i = Math.round(Math.random() * this.items.length)
this.items.splice(i, 0, id++)
},
reset() {
this.items = getInitialItems()
},
shuffle() {
this.items = _.shuffle(this.items)
},
remove(item) {
const i = this.items.indexOf(item)
if (i > -1) {
this.items.splice(i, 1)
}
},
},
}).mount('#app')
</script> </script>
<style> <style>
.container { .container {
position: relative; position: relative;
padding: 0; padding: 0;
} }
.item { .item {
width: 100%; width: 100%;
height: 30px; height: 30px;
background-color: #f3f3f3; background-color: #f3f3f3;
border: 1px solid #666; border: 1px solid #666;
box-sizing: border-box; box-sizing: border-box;
} }
/* 1. declare transition */ /* 1. declare transition */
.fade-move, .fade-enter-active, .fade-leave-active { .fade-move,
transition: all .5s cubic-bezier(.55,0,.1,1); .fade-enter-active,
} .fade-leave-active {
/* 2. declare enter from and leave to state */ transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
.fade-enter-from, .fade-leave-to { }
opacity: 0; /* 2. declare enter from and leave to state */
transform: scaleY(0.01) translate(30px, 0); .fade-enter-from,
} .fade-leave-to {
/* 3. ensure leaving items are taken out of layout flow so that moving opacity: 0;
transform: scaleY(0.01) translate(30px, 0);
}
/* 3. ensure leaving items are taken out of layout flow so that moving
animations can be calculated correctly. */ animations can be calculated correctly. */
.fade-leave-active { .fade-leave-active {
position: absolute; position: absolute;
} }
</style> </style>

View File

@ -33,10 +33,10 @@
</transition> </transition>
</script> </script>
<script> <script>
const Modal = { const Modal = {
template: '#modal-template', template: '#modal-template',
props: ['show'] props: ['show'],
} }
</script> </script>
<!-- modal container that lives outside of app root --> <!-- modal container that lives outside of app root -->
@ -56,57 +56,57 @@ const Modal = {
</div> </div>
<script> <script>
Vue.createApp({ Vue.createApp({
components: { Modal }, components: { Modal },
data: () => ({ data: () => ({
showModal: false showModal: false,
}) }),
}).mount('#app') }).mount('#app')
</script> </script>
<style> <style>
.modal-mask { .modal-mask {
position: fixed; position: fixed;
z-index: 9998; z-index: 9998;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: rgba(0, 0, 0, .5); background-color: rgba(0, 0, 0, 0.5);
display: table; display: table;
transition: opacity .3s ease; transition: opacity 0.3s ease;
} }
.modal-wrapper { .modal-wrapper {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
} }
.modal-container { .modal-container {
width: 300px; width: 300px;
margin: 0px auto; margin: 0px auto;
padding: 20px 30px; padding: 20px 30px;
background-color: #fff; background-color: #fff;
border-radius: 2px; border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, .33); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
transition: all .3s ease; transition: all 0.3s ease;
font-family: Helvetica, Arial, sans-serif; font-family: Helvetica, Arial, sans-serif;
} }
.modal-header h3 { .modal-header h3 {
margin-top: 0; margin-top: 0;
color: #42b983; color: #42b983;
} }
.modal-body { .modal-body {
margin: 20px 0; margin: 20px 0;
} }
.modal-default-button { .modal-default-button {
float: right; float: right;
} }
/* /*
* The following styles are auto-applied to elements with * The following styles are auto-applied to elements with
* transition="modal" when their visibility is toggled * transition="modal" when their visibility is toggled
* by Vue.js. * by Vue.js.
@ -115,17 +115,17 @@ Vue.createApp({
* these styles. * these styles.
*/ */
.modal-enter-from { .modal-enter-from {
opacity: 0; opacity: 0;
} }
.modal-leave-to { .modal-leave-to {
opacity: 0; opacity: 0;
} }
.modal-enter-from .modal-container, .modal-enter-from .modal-container,
.modal-leave-to .modal-container { .modal-leave-to .modal-container {
-webkit-transform: scale(1.1); -webkit-transform: scale(1.1);
transform: scale(1.1); transform: scale(1.1);
} }
</style> </style>