HelloGitHub- xiaoxia

When it comes to VS Code, the general impression is something like this: it’s just an editor, and the most important thing is the pleasure of coding.

Many of the functions in the code should focus on how to improve coding efficiency, reduce coding error rate, free up the labor of the coder, etc., as for things outside the code, such as preview, the browser.

So maybe not many people associate VS Code with WebView.

The ubiquitous WebView

But I’m sure you’ve seen it in many of the “famous” VS Code plug-ins. For example, VS Code-DRAwio can draw a flowchart in VS Code:

GitHub address: github.com/hediet/vsco…

Vscode – LeetCode:

GitHub address: github.com/LeetCode-Op…

There is also a “leek-fund” of wealth and freedom from a “small leek” while fishing at work.

GitHub address: github.com/LeekHub/lee…

So you can see, with WebView to expand capabilities, the plug-in market will become “a hundred flowers bloom”, can meet the needs of all kinds of people all kinds of fishing. However, the success of the above open source projects is not only based on the simple WebView capabilities described in this article. If you are interested in the above open source projects, you can clone the Code directly and take a look at it, and you may have the next great open source VS Code plug-in.

What is a WebView

As mentioned earlier, VS Code allows us to customize a lot of features under its rules, but the scope of our customization for views is very small, which limits the creativity of programmers. However, the free soul will not be defeated by the difficulties in front of us, so the heart and heart of the peer mutual pity is the birth of WebView.

Of course this is all part of my own OS, but certainly the WebView API exists to allow extensions to create fully customizable views in VS Code. For example, the built-in Markdown extension uses Webviews to render a Markdown preview. Webviews can also be used to build complex user interfaces that go beyond VS Code’s native API support.

You can also simply think of a WebView as an IFrame within VS Code. The WebView can render almost any HTML content in this framework, and can also communicate with extensions using messaging. This freedom makes Webviews very powerful and gives it a whole new scope to extend.

Create a simple WebView

From the first example you should get an idea of how powerful the Extension of WebView can be, not only as a view for a custom editor to extend and provide a custom UI to edit any file in the workspace. It also allows you to continue rendering the WebView in the WebView in the sidebar or panel area, and so on.

If you are interested, you can go to the official website to continue learning. Today we will focus on the simplest way: creating a simple WebView panel in the editor.

1. Configure commands

The first step is to configure the command. Open the package.json file again and configure a new command:

"contributes": {
		"commands": [...// omit other commands
			{
        "command": "webview.start"."title": "open a webview page"."category": "HelloGitHub webview"}],...// Omit other configuration items
}
Copy the code

After configuration, register the new command in extension.js:

function activate(context) {...// Omit other command registrations
  
	const webviewCommand = vscode.commands.registerCommand('webview.start'.() = > {
    // Create and display a WebView
    const panel = vscode.window.createWebviewPanel(
      'hgWebview'.// Define the type of webView used internally
      'HelloGitHub webview'.// Show the user the title
      vscode.ViewColumn.One, // Display the webView in the column editor
      {} // Other Webview configurations.
    );
  });

	context.subscriptions.push(webviewCommand); // You can put more than one
}
Copy the code

Take a look at the configuration and let’s run our plugin:

You can see that the title is the “HelloGitHub webView” we configured above on package.json.

Let’s see what these values are:

Look not to understand? Extension. Js = extension.js = extension.js

const webviewCommand = vscode.commands.registerCommand('webview.start'.() = > {
  const panel = vscode.window.createWebviewPanel(
    'hgWebview'.'HelloGitHub webview',
    vscode.ViewColumn.Two, // Change from One to Two{}); });Copy the code

The effect is as follows:

It doesn’t make sense to add a js file, because it would be the same if it didn’t occupy the first ViewColumn in the editor, and our WebView would open in the second column. Are these words very simple to understand?

2. Initialize the content

Now we’re going to get to the most important part, how do you enrich your WebView? It’s really easy, just think of it as an iframe, which is just HTML stuff, right? So easy!

First we need to have a separate file that contains the entire HTML content. I’ve put it here to make it easier to distinguish:

Configure a very simple web page content with only one image:

module.exports = ` 
         
       
       Hello GitHub      `
Copy the code

Importing the file in extension.js and configuring it into our WebView:

const hgWebview = require('./webview/hello-github'); .const webviewCommand = vscode.commands.registerCommand('webview.start'.() = > {
    const panel = vscode.window.createWebviewPanel(
      'hgWebview'.'HelloGitHub webview',
      vscode.ViewColumn.One,
      {}
    );
    panel.webview.html = hgWebview; // Yes, this is the configuration, very simple}); .Copy the code

Take a look at the results:

A word of caution here is that your configuration should always be a complete HTML document. HTML fragments or badly formatted HTML can lead to unsuccessful runs, so be careful to debug when performing complex operations and keep an eye on the control bar.

3. Update content

Yes, we are now going to update the WebView from the editor! For example, we add a line of text to the WebView, and then add a timer in the editor to dynamically modify it. First, modify our HTML file, it is no longer static text, it must receive a variable to move, so change it to a function:

module.exports = (txt) = > {
  return ` <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Initial scale=1.0"> <title>Hello GitHub</title> </head> <body> <img src="https://cdn.jsdelivr.net/gh/521xueweihan/img_logo@main/logo/readme.gif" width="300" /> <div>${txt}</div> </body> </ HTML > '
}
Copy the code

Second, we need to interact with this function and pass in the value we are going to show, and this value will be changed at timing 1s, so it looks like this:

const hgWebviewFun = require('./webview/hello-github');

// Set up our copy
const webviewTxt = {
  'descripton': 'HelloGitHub is an open source organization that loves open source projects. '.'slogon': 'We don't have money, but we have dreams! '}; .const webviewCommand = vscode.commands.registerCommand('webview.start'.() = > {
		const panel = vscode.window.createWebviewPanel(
			'hgWebview'.'HelloGitHub webview',
			vscode.ViewColumn.One,
			{}
		);

		let iteration = 0;
		const updateWebview = () = > {
      // Make a simple judgment for the value
			const key = iteration++ % 2 ? 'descripton' : 'slogon';
			panel.title = webviewTxt[key];
			panel.webview.html = hgWebviewFun(webviewTxt[key]);
		};

		// Set the initialization content
		updateWebview();

		// Set a simple timer to run once a second
		setInterval(updateWebview, 1000); }); .Copy the code

Take a look at our results and see if it becomes a dynamic web page:

But the effect is implemented. Do you notice that the method we implemented is very “violent”, replacing the entire HTML content directly, similar to reloading iframe. So if you switch to a complex page, performance is a very serious issue, which can lead to a lot of performance problems. And when the user closes the WebView panel, the WebView itself is destroyed. An exception is raised if you try to use a destroyed WebView, such as our setInterval above, which continues to fire and update panel.webView.html.

So we want to avoid that:

const webviewCommand = vscode.commands.registerCommand('webview.start'.() = > {
  const panel = vscode.window.createWebviewPanel(
    'hgWebview'.'HelloGitHub webview',
    vscode.ViewColumn.One,
    {}
  );

  let iteration = 0;
  const updateWebview = () = > {
    const key = iteration++ % 2 ? 'descripton' : 'slogon';
    panel.title = webviewTxt[key];
    panel.webview.html = hgWebviewFun(webviewTxt[key]);
  };

  updateWebview();
  const interval = setInterval(updateWebview, 1000);

  panel.onDidDispose(
    () = > {
      // Remove subsequent updates to the webView when closing it
      clearInterval(interval);
    },
    null,
    context.subscriptions
  );
});
Copy the code

4. Messaging

As mentioned earlier, you can simply think of webViews as iframes, which also means that they can run scripts. However, JavaScript is disabled in WebView by default, you can enable it by passing enableScripts: true. However, the WebView website recommends that webViews always use content security policies to disable inline scripting, so we won’t expand it here. But that doesn’t stop us at all from doing what WebView does best: messaging.

The WebView debugging

Before passing on the message, I thought it was worth mentioning the debugging tool command Developer: Toggle Developer Tools. You can invoke this developer debug command via comand+ P (MacOS), which will help you debug WebView like a duck in water, easily catch exceptions and fix them

You can also look at the STRUCTURE of the DOM in Elements, which is pretty familiar

The WebView receives the message

First let’s look at how to pass messages from our plug-in application to our WebView. You must have guessed, didn’t you? Yes, postMessage!

Modify our registration command as follows:

  • Save the createWebviewPanel variable to a new variable

  • A new command for messaging, webview.dorefactor, was added

  • And because we need to listen for messages inside the HTML, we have to make sure to turn on the script, which is enableScripts:true

  • To make sure we’re not dazzled, we’ve also removed the setInterval timer

.let currentPanel; // Redefine a variable for use between multiple commands
	const webviewCommand = vscode.commands.registerCommand('webview.start'.() = > {
		currentPanel = vscode.window.createWebviewPanel(
			'hgWebview'.'HelloGitHub webview',
			vscode.ViewColumn.One,
			{
				enableScripts: true // Enable the js script permission});let iteration = 0;
		const updateWebview = () = > {
			const key = iteration++ % 2 ? 'descripton' : 'slogon';
			currentPanel.title = webviewTxt[key];
			currentPanel.webview.html = hgWebviewFun(webviewTxt[key]);
		};

		updateWebview();
		// const interval = setInterval(updateWebview, 1000); Remove timer

		currentPanel.onDidDispose(
			() = > {
				// clearInterval(interval); Remove timer
				currentPanel = undefined; // Release variables when the webView is destroyed
			},
			null,
			context.subscriptions
		);
	});

 // Register a new command
	const webviewRefactorCommand = vscode.commands.registerCommand('webview.doRefactor'.() = > {
		if(! currentPanel) {return;
		}

		// Send a message to the webView
		// You can send any JSON serialized data
		currentPanel.webview.postMessage({ command: 'refactor'.msg: 'Please pay more attention to us.'}); }) context.subscriptions.push(webviewCommand, webviewRefactorCommand); .Copy the code

In case anyone misses this step, I decided to remind you to add the newly registered command to package.json:

. {"command": "webview.start"."title": "open a webview page"."category": "HelloGitHub webview"
      },
			{
        "command": "webview.doRefactor"."title": "doRefactor a webview page"."category": "HelloGitHub webview"}...Copy the code

Have the message to send, of course also need to have the message to receive! So we need to modify our HTML file to add a listener for receiving messages:

module.exports = (txt) = > {
  return ` <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Initial scale=1.0"> <title>Hello GitHub</title> </head> <body> <img src="https://cdn.jsdelivr.net/gh/521xueweihan/img_logo@main/logo/readme.gif" width="300" /> <h1 id="message-show">hello</h1> <div>${txt}</div> <script> const box = document.getElementById('message-show'); Window.addeventlistener ('message', event => {const message = event.data; Switch (message.com) {case 'refactor': mand (message.com) {case 'refactor': box.textContent = message.msg; break; }}); </script> </body> </html> `
}
Copy the code

Open the developer debugging tool, first with the webView. Start command to open the webView:

After running webView.dorefactor, we upload our value to the webView:

The WebView sends the message

The WebView can also pass messages back to our extension.

This is done using special VS Code API objects in the postMessage of the WebView. To access VS Code API objects, you need to call the acquireVsCodeApi function inside the WebView only once per session.

And you must keep the VS Code API instance returned by this method and distribute it to any other function that needs to use it.

We can use the VS Code API’s postMessage method to display messages from the WebView in our plug-in:

const vscode = acquireVsCodeApi(); // Use it directly

vscode.postMessage({ // Send a message
  command: 'alert'.text: '🚀 sent successfully ~ thanks old iron ~'
})
Copy the code

We tied the event trigger to a new button. The complete code is as follows:

module.exports = (txt) = > {
  return ` <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Initial scale=1.0"> <title>Hello GitHub</title> </head> <body> <img src="https://cdn.jsdelivr.net/gh/521xueweihan/img_logo@main/logo/readme.gif" width="300" /> <h1 id="message-show">hello</h1> <div>${txt}</div> <button id="btn_submit"> </button> <script> const box = document.getElementById('message-show'); const vscode = acquireVsCodeApi(); window.addEventListener('message', event => { const message = event.data; console.log(message) switch (message.command) { case 'refactor': box.textContent = message.msg; break; }}); document.getElementById('btn_submit').addEventListener('click', function(){ vscode.postMessage({ command: 'alert' text: '🚀 send success thank brother ~ ~'})}) < / script > < / body > < / HTML > `
}
Copy the code

We also need to receive messages from the WebView in our plug-in code:

. currentPanel.webview.onDidReceiveMessage(message= > {
    switch (message.command) {
      case 'alert':
        vscode.window.showInformationMessage(message.text);
        return; }},undefined, context.subscriptions ); .Copy the code

The complete code is as follows, and the event binding should be done when opening the WebView:

.const webviewCommand = vscode.commands.registerCommand('webview.start'.() = > {
		currentPanel = vscode.window.createWebviewPanel(
			'hgWebview'.'HelloGitHub webview',
			vscode.ViewColumn.One,
			{
				enableScripts: true});let iteration = 0;
		const updateWebview = () = > {
			const key = iteration++ % 2 ? 'descripton' : 'slogon';
			currentPanel.title = webviewTxt[key];
			currentPanel.webview.html = hgWebviewFun(webviewTxt[key]);
		};

		updateWebview();
		// const interval = setInterval(updateWebview, 1000);

		currentPanel.onDidDispose(
			() = > {
				// clearInterval(interval);
				currentPanel = undefined;
			},
			null,
			context.subscriptions
		);

		// Process messages from webView
		currentPanel.webview.onDidReceiveMessage(
			message= > {
				switch (message.command) {
					case 'alert':
						vscode.window.showInformationMessage(message.text);
						return; }},undefined, context.subscriptions ); }); .Copy the code

Let’s take a look at the style before clicking the button:

Let’s see what happens when we click the button.

Four,

That happy time is always short, and it’s the end of the article. In general, WebView is like iframe in VS Code. It may have a few performance drawbacks, but it can help us achieve a lot of rich and interesting things.

Therefore, we should make good use of this function and exert its power to the maximum. According to the description on the official website, we should also pay more attention to the following points when using:

  • A WebView should have the minimum set of features it needs. For example: Do not set enableScripts: true if you do not need to run the script

  • Webviews are strictly compliant with content security policies, so there are limits to what can be loaded and executed in a WebView. For example, a content security policy can ensure that only lists of scripts are allowed to run in a WebView, or even tell the WebView to load only HTTPS images.

  • For security reasons, WebView cannot directly access local resources by default. It runs in an isolated context. To load local images, JS, CSS, etc., you must use the special VS code-resource: protocol.

  • As normal web pages require, you must clean up all user input when building HTML for the WebView. Failure to clean up input properly can result in content injection, which can expose your users to security risks. For example: file content, file and folder paths, user and workspace Settings

  • WebView has its own life cycle. If it plays its maximum role in the scenario of extreme experience, it is suggested to go to the official website for further study

Finally, the next “VS Code” article, the last one in this introductory series, will give you a more comprehensive experience. Give me a little more time to study it and look forward to seeing you next time.