-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(compiler-core): whitespace handling strategy #1600
Conversation
Actually I’ve been thinking maybe we should have a |
What's the actual use for this? @Justineo can you summarize the differences between condense mode and JSX whitespace handling? |
|
In my project, I used the parser of vue compiler to do some template conversion work, but found that the whitespaces and newlines were ignored in the final AST. So I hope the compiler can provide this strategy configuration item. |
@CodeDaraW I think that's a valid use case and I was bringing up a related issue. |
Can you be more specific? Are you converting legacy HTML to Vue templates or doing transforms on Vue templates? Why would you need the whitespace to be preserved? |
The ignored white space doesn't affect the output for browser at all. It's kinda like an optimization without any side effects. |
I've actually been struggling with whitespace issues as well. It's not expected that mustache syntax preserves whitespace, while elements do not when passed to a slot. <SlottedComponent>
{{ 'this will preserve whitespace (including spaces and newline after component tag)' }}
</SlottedComponent> <SlottedComponent>
<span>This will ignore whitespace</span>
</SlottedComponent> |
Sorry to reply now, I've been busy these days. Actually this may be more related to HTML, the situation is more complicated and I will try to make it simple: Given a HTML fragment: <div id="1">
<div id="2"></div>
<div id="3"></div>
<div id="4"></div>
</div> With the parser I can get an AST, do tree traversal to mark the tree nodes, and then make changes to the source HTML. For example, I want to remove all the nodes between div#2 and div#4(including spaces and lines) with the help of node location info. Since the new line nodes are ignored, I can only remove the div#3 and remain the new line nodes like: <div id="1">
<div id="2"></div>
<div id="4"></div>
</div> If the new line nodes info is provided, the new line nodes can be identified and removed(or remained up to me): <div id="1">
<div id="2"></div><div id="4"></div>
</div> I'm not sure if I have clearly described the problem, and if it goes against the original intention of the design or brings other effects. |
Any news on when this? This causes a problem for me as I render my code on the server (for SEO purposes) and then parse it with Vue which then changes the white-space and causes multiple issues. I'm going to hold out from upgrading until this issue is resolved. |
Do we have a timeframe for this feature to be released? |
ec78f3b
to
73fc92b
Compare
I already commented on a closed issue but just so it’s not ignored, please see my post over at stack overflow https://stackoverflow.com/questions/64432182/vue-3-removes-white-space-between-inline-block-elements which shows how this is a breaking change from v2 and how it affects the rendered output contrary to what’s been mentioned above. |
What’s the deal with this? Does Vue really remove whitespace between inline elements, and there’s no way to configure that behaviour? As I’m using Vue 3 with Laravel, and it’s kinda breaking web pages. Browsers have rendered whitespace between inline elements for literally decades. So what’s the case for changing that behaviour? Example screenshot: You can see there’s no whitespace around the “or” text. |
Any one…? |
@martinbean same. I ended up applying this pull request to my local build just so I could continue to use Vue 3. |
In the meantime, you can inject whitespace with this expression: |
@CyberAP My issue is, if I mount a Vue component on a wrapper div (usually This changing of browser’s default whitespace handling is just unacceptable in my opinion. |
Painful. This has really broken an application I attempted to upgrade to Vue 3. |
@abalashov Yup. This makes Vue 3 completely unusable for me. |
@abalashov and @martinbean I agree, Vue 3 is unusable without this fix. |
@yyx990803 Can we please get an official response from someone on this issue? Vue should not be usurping browser default behaviour of inline element spacing. |
@martinbean Agreed. I don't mean to be insolent or seem entitled - it's open-source - but it's completely broken a UIkit-styled application. All the buttons are smashed together, as are some other kinds of UI widgets. If it were a question of changed defaults, I could change the behaviour, but if there's no support for the necessary behaviour anymore, that's a serious problem. |
See https://codesandbox.io/s/jolly-poincare-ibywi?file=/src/App.vue Whitespace-only text nodes will be removed only when they include line feeds. If something is treated as inline content, just put them in the same line and it should just work. /cc @vuejs/docs I think we should put this into the docs so that people won't get confused. |
73fc92b
to
1c42f3a
Compare
@HcySunYang I've fixed the conflict. |
@CodeDaraW Thanks~ |
- discard leading/ending whitespace inside an element - condense preserved whitesapce into single space
@yyx990803 Thanks for moving this along. Do we have a timeframe of when v3.1.0 will be released? |
This PR has been cherry picked into working branch via dee3d6a. Thanks! |
@yyx990803, I've tried to add this in the vue.config.js, like in Vue 2 (vuejs/vue#10485 (comment)): chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.compilerOptions.whitespace = 'preserve'
return options
})
} but Looking at this file dee3d6a#diff-4c790a7767e4960284e4847ad5795feb31ca94d74a138e81a90b7c044e20df6cR68, I also thought I could possibly add it in the createApp params, but it seems I can't even get the delimiters to work: createApp({
render: () => h(App),
delimiters: ['${', '}'],
whitespace: 'preserve'
}) Anyone else made it work? vuejs/vue-cli#1020 (comment) Reproducible on this simple library: https://github.com/antoniandre/simple-syntax-highlighter/blob/next/vue.config.js |
I have it working on all components in my app, my vite.config.js says import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: {
whitespace: "preserve",
} as unknown,
},
}),
],
}); (using and my package.json has dev dependency |
@areve, it's great to see you got it working, thanks for sharing your config! 👍 I still can't make it work with slots though. The whitespaces (new lines in particular), are preserved when inside a pre tag, but not in a custom component. I've placed this in the vue.config.js but it doesn't help: export default {
publicPath: 'truc',
chainWebpack: (config) => {
config.module
.rule("vue")
.use("vue-loader")
.loader("vue-loader")
.tap(options => {
// https://github.com/vuejs/vue-next/pull/1600
options.compilerOptions.whitespace = "preserve";
return options
})
}
} |
I had a play with your CodeSandbox and I agree there does appear to be a problem here. The whitespace in slots is not preserved as I would expect. I suggest this is an oversight and should be logged as a new bug. The lack of documentation is also an issue, I'm not sure how/who/when this may get changed. |
Has any one gotten this working with Laravel Mix yet? No matter how I try and pass these options, whitespace still gets removed from inline elements when mounting view to a containing element, like |
@martinbean This should work for SFCs: mix.vue({
options: {
compilerOptions: {
whitespace: "preserve",
},
},
}) But you'll also need to configure the runtime compiler if you're using it: app.config.compilerOptions.whitespace = "preserve" |
@thecrypticace Thanks. I have no idea what I’m doing wrong, but I just can’t get Vue 3 to work, at all. I just seem to encounter issue after issue. This is what I have as a reduced test case: <!-- resources/views/welcome.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
<script defer src="{{ asset('js/app.js') }}"></script>
</head>
<body>
<div id="app">
<div class="text-center">
<a class="btn btn-primary" href="https://google.com/" rel="external">Google</a>
<a class="btn btn-primary" href="https://bing.com/" rel="external">Bing</a>
</div>
<div class="text-center">
<test-component></test-component>
</div>
</div>
</body>
</html> // resources/js/app.js
import { createApp } from 'vue';
const app = createApp();
app.component('test-component', () => import('./components/TestComponent.vue'));
app.mount('#app'); // webpack.mix.js
const mix = require('laravel-mix');
mix
.js('resources/js/app.js', 'public/js')
.vue({
options: {
compilerOptions: {
whitespace: 'preserve'
}
},
version: 3
}); <!-- resources/js/components/TestComponent.vue -->
<template>
<div>
<a class="btn btn-secondary" href="https://yahoo.com/">Yahoo!</a>
<a class="btn btn-secondary" href="https://duckduckgo.com/">DuckDuckGo</a>
</div>
</template> Vue 3 just refuses to work. I get this in the console:
It seems to be pointed at an issue with my What’s the issue here? I’m just a little frustrated that I can’t seem to get past the first hurdle of just rending a single test component, let alone building anything with Vue 3 😢 And I so dearly want to use that lovely TypeScript goodness and Composition API! Installed versions:
|
OK, got Vue compiling, but I’m afraid this issue is still persisting. Take a look at the screenshot below. The whitespace is honoured in the Vue single-file component itself, but any whitespace in my <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
<script defer src="{{ asset('js/app.js') }}"></script>
</head>
<body>
<div id="app">
<div class="text-center">
<a class="btn btn-primary" href="#">Button One</a>
<a class="btn btn-primary" href="#">Button Two</a>
</div>
<test-component></test-component>
</div>
</body>
</html> It’s definitely Vue, because when I force-refresh the page I can see the whitespace until the JavaScript is parsed and then it’s collapsed. Why is Vue doing this? It’s a fundamental browser behaviour Vue is overriding here. |
I found an alternative to this by deep-diving Vue compiler options. simply this: const oldPreTagFunc = conf.build.vueLoaderOptions.compilerOptions.isPreTag
vueLoaderOptions.compilerOptions.isPreTag = (tag) => tag === 'pre'
|| tag === 'q-markdown'
|| (typeof oldPreTagFunc === 'function' ? oldPreTagFunc(tag) : false) Where my component that needs the whitespace handling starts with |
try remove |
For those using vue-cli, the correct syntax for vue.config.js is as follows: chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
// https://github.com/vuejs/vue-next/pull/1600
options.compilerOptions = {
whitespace: 'preserve'
};
return options
})
} |
It works for me. Thank you! |
fix this issue by compilerOptions.whitespace: 'preserve' will conflict |
In some cases, we may want to preserve whitespaces in the HTML template, but vue compiler only provides condense strategy currently.
This PR will make compiler support whitespace handling strategy like v2.