使用Vue.js创建GitHub File Explorer

Vue.js makes everything easy. Creating seemingly complex applications becomes super simple and is something that can be done in much less time than previously. We will demonstrate this by creating a Github file explorer that will allow us to explore files in public Github repos.

Vue.js使一切变得容易。 创建看似复杂的应用程序变得非常简单,并且可以用比以前更少的时间来完成。 我们将通过创建一个Github文件浏览器来演示这一点,该浏览器将使我们能够浏览公共Github存储库中的文件。

All the code for this post can be found on Github. You can view a demo here.

这篇文章的所有代码都可以在Github上找到。 您可以在此处查看演示

设置开发环境 (Setup Development Environment)

The repo for this post has a good starting point tagged. We need to pull the code down and check out this tag. Then we need to download all the dependencies using npm. Run the following commands in your terminal of choice.

这篇文章的回购有一个很好的起点标记。 我们需要下拉代码并签出此标签。 然后,我们需要使用npm下载所有依赖项。 在您选择的终端中运行以下命令。

git clone https://github.com/scotch-io/vuejs-github-explorer.git
    cd vuejs-github-explorer
    git checkout start-here
    npm install
    ./node_modules/.bin/gulp serve

This will get your local environment setup and serve the starting point files on your localhost on port 8080. Open up a browser window and go to localhost:8080. You should see something like below.

这将设置您的本地环境,并在本地主机上的端口8080上提供起点文件。打开浏览器窗口,然后转到localhost:8080 。 您应该看到类似下面的内容。

starting-point

As you can see, there isn't anything special going on here just yet. So far, all we have done is compile Twitter Bootstrap, Font Awesome, and some other basic styles that will be used in the application. We have also compiled our JavaScript using Browserify. You can see the JavaScript in src/js/app.js. We are only logging a simple "Hello World" to the console. All this is handled by Gulp. If you are interested in seeing what all is going on, take a look at the Gulpfile.js in the root directory of the project.

如您所见,这里还没有什么特别的事情。 到目前为止,我们所做的只是编译Twitter Bootstrap,Font Awesome以及将在应用程序中使用的其他一些基本样式。 我们还使用Browserify编译了JavaScript。 您可以在src/js/app.js看到JavaScript。 我们仅将一个简单的“ Hello World”记录到控制台。 所有这些都由Gulp处理。 如果您有兴趣了解所有情况,请查看项目根目录中的Gulpfile.js

Now that we have a development environment setup, let's make something cool.

现在我们有了开发环境的设置,让我们做点很酷的事情。

创建Vue实例 (Creating the Vue instance)

First things first. We need to create the HTML for the main part of our application. Input the following HTML into src/index.html.

首先是第一件事。 我们需要为应用程序的主要部分创建HTML。 将以下HTML输入到src/index.html

<div id="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form id="changeRepoForm" @submit.prevent="changeRepo()" class="form-inline">
                <div class="form-group">
                    <label for="fullRepoName">Full Repo Name</label>
                    <input type="text" name="fullRepoName" v-model="fullRepoName" class="form-control">
                </div>
                <input type="submit" class="btn btn-default" value="Get repo filesystem!">
            </form>
            <hr>
        </div>
    </div>
</div>

Here, we are simply creating a form and marking it up with some basic Bootstrap code. On the form, we have used the @submit.prevent directive to add a submit listener to the form. The @submit adds the listener while the .prevent part will automatically run preventDefault for us. When we submit the form, Vue will fire the changeRepo method. We will get to this method in a minute.

在这里,我们只是创建一个表单,并使用一些基本的Bootstrap代码对其进行标记。 在表单上,​​我们使用了@submit.prevent指令将提交侦听器添加到表单。 @submit添加侦听器,而.prevent部分将为我们自动运行preventDefault 。 当我们提交表单时,Vue将触发changeRepo方法。 一分钟后,我们将介绍这种方法。

The form itself is pretty basic. It has a text field with a label and a submit button. The input has the v-model directive on it. This creates a two-way binding between the value of the input and the value of fullRepoName, which is a piece of data on our Vue instance.

表单本身是非常基本的。 它具有带标签和提交按钮的文本字段。 输入上带有v-model指令。 这将在输入的值和fullRepoName的值之间创建双向绑定, fullRepoName是Vue实例上的一条数据。

Now that we have this set up, let's create our Vue instance. Remove all the code from src/app.js and input the following:

现在我们已经设置好了,让我们创建Vue实例。 从src/app.js删除所有代码,然后输入以下内容:

var Vue = require('vue');
    Vue.config.debug = true;

Here we are requireing Vue into our file using "Node syntax." We are able to do this due to our use of Browserify as part of our Gulp pipeline. The next thing we do is set a global config option to put our application in "debug mode." This just means that if we get an error, we will get more information about it in the developer console.

在这里,我们require使用“节点语法”将Vue放入文件中。 由于能够将Browserify用作Gulp管道的一部分,因此我们能够做到这一点。 接下来要做的是设置一个全局配置选项,以使我们的应用程序处于“调试模式”。 这只是意味着,如果出现错误,我们将在开发人员控制台中获得有关此错误的更多信息。

new Vue({
        el: '#container',
        data: {
            fullRepoName: '',
            username: '',
            repo: ''
        },
        methods: {
            changeRepo: function() {
                var splitData = this.fullRepoName.split('/');
                this.username = splitData[0];
                this.repo = splitData[1];

                console.group("Vue Data");
                console.log("fullRepoName:", this.fullRepoName);
                console.log("username:", this.username);
                console.log("repo:", this.repo);
                console.groupEnd("Vue Data");
            }
        }
    });

This is the code that creates our Vue instance. We bind the instance to the element with the ID of container. Then we initialize our instance with some data. It is best to initialize Vue instances with the data that you plan on using in it. This ensures that Vue binds to all the data we want it to. Lastly, we define the methods we will use in our Vue instance. Here is where we will define the changeRepo method that we used in our HTML earlier.

这是创建我们的Vue实例的代码。 我们将实例绑定到具有container ID的元素。 然后,我们使用一些数据初始化实例。 最好使用计划在其中使用的数据来初始化Vue实例。 这样可以确保Vue绑定到我们想要的所有数据。 最后,我们定义将在Vue实例中使用的方法。 在这里,我们将定义我们先前在HTML中使用的changeRepo方法。

The changeRepo method simply splits the value of the fullRepoName piece of data and assigns the first part to username and the second part to repo. This is possible due to the two-way binding I mentioned earlier. We also print the data on our instance to the console so we can see it change as we submit our form.

changeRepo方法只是拆分fullRepoName数据块的值,并将第一部分分配给username ,第二部分分配给repo 。 由于我前面提到的双向绑定,所以这是可能的。 我们还将实例上的数据打印到控制台,以便我们在提交表单时可以看到它们的变化。

only-form

Go ahead and view the page in your browser. The Gulp task compiles the code as you write it and will refresh the browser for you also. Open dev tools and view the console. Now type the name of a public Github repository. For example, try typing laravel/laravel in the text box and hit submit. You should see the data spit out in a nice, readable form in the console. Pretty neat, huh?

继续并在浏览器中查看页面。 Gulp任务会在您编写代码时对其进行编译,并且还会为您刷新浏览器。 打开开发人员工具并查看控制台。 现在输入公共Github存储库的名称。 例如,尝试在文本框中键入laravel/laravel ,然后单击“提交”。 您应该在控制台中看到数据以一种易于阅读的形式吐出。 很整洁吧?

This all is fine and dandy, but let's start displaying the files for the repository we type in.

一切都很好,但让我们开始显示所键入存储库的文件。

文件浏览器 (The File Explorer)

We will implement the explorer as a separate component. Separating applications into smaller components makes code easier to comprehend and maintain. First, let's create a file at src/js/components/github-file-explorer/index.js. This will be the main entry for our component. Before we define anything, let's tell our Vue instance about this new component. Add the following code to our Vue instance.

我们将把资源管理器实现为一个单独的组件。 将应用程序分成较小的组件可以使代码更易于理解和维护。 首先,让我们在src/js/components/github-file-explorer/index.js上创建一个文件。 这将是我们组件的主要条目。 在定义任何内容之前,让我们告诉我们有关此新组件的Vue实例。 将以下代码添加到我们的Vue实例中。

components: {
        githubFileExplorer: require('./components/github-file-explorer')
    }

This brings in our component from the given file. Since no specific file is provided, the default used is index.js.

这从给定文件中引入了我们的组件。 由于未提供任何特定文件,因此使用的默认值为index.js

In the index.js file of our component, we need to export an object that has all the configurations for our component. Put the following in that file.

在组件的index.js文件中,我们需要导出一个具有组件所有配置的对象。 将以下内容放入该文件中。

module.exports = {
        template: require('./template.html'),
        data: function() {
            return {
                path: '/',
                files: []
            };
        },
        props: {
            username: {
                type: String,
                required: true
            },
            repo: {
                type: String,
                required: true
            }
        },
        created: function() {
            if (this.username && this.repo) this.getFiles();
        }
    };

Here we are exporting the configuration object. We are defining a template and bringing in code from a template.html file in the same directory. We can bring in HTML files like this thanks to a Browserify transform called Partialify. Browserify has a ton of other transforms you can use in your projects. We will create this template file in a minute. Next we define the data this component will need. It will track the current path and the files we receive from Github.

在这里,我们导出配置对象。 我们正在定义一个模板,并从同一目录中的template.html文件引入代码。 我们可以在HTML文件中把这样得益于Browserify变换称为Partialify 。 Browserify还可以在项目中使用大量其他转换 。 我们将在一分钟内创建此模板文件。 接下来,我们定义该组件将需要的数据。 它将跟踪当前路径以及我们从Github收到的文件。

We then define the props this component will accept from its parent. Because we don't want to make requests to Github if the username or repo are blank, we define them in the component as objects. This allows us to add validations that the props must pass for the data in the Vue instance to be updated. We define both these props as strings and required.

然后,我们定义该组件将从其父对象接受的道具 。 因为如果用户名或存储库为空,我们不想向Github发出请求,所以我们在组件中将它们定义为对象。 这使我们可以添加验证,道具必须通过验证才能更新Vue实例中的数据。 我们将这两个道具都定义为字符串和必需项。

Lastly, we add a created method that calls getFiles if username and repo are set. This created method is a lifecycle method. This means it will be called when that particular point in the component's lifecycle is hit. Since it calls getFiles, let's create this method. Add the following code to this component.

最后,如果设置了username和存储repo ,我们将添加一个created方法,该方法将调用getFiles 。 此created方法是生命周期方法 。 这意味着在命中组件生命周期中的特定点时将调用它。 由于它调用getFiles ,因此我们创建此方法。 将以下代码添加到此组件。

methods: {
        getFiles: function() {
            this.$http.get('https://api.github.com/repos/' + this.fullRepoUrl + '/contents' + this.path,
                function(data) {
                    this.files = data;
                }
            );
        }
    }

Here we are defining getFiles. This method hits the Github API for us and returns the files in the repository for the path specified. We are using this.$http to make the call. That is provided by vue-resource, a plugin for Vue that allows us to make RESTful calls from inside our Vue instance. We could also use jQuery or something similar to do the same thing. However, I prefer to use vue-resource since it is made for Vue. For it to work though, we need to tell Vue to use it. We have already brought it in as a dependency in the package.json in this project. Let's tell Vue about it. Put the following in your src/app.js underneath where we turned on debugging.

在这里,我们定义了getFiles 。 该方法为我们命中了Github API,并返回存储库中指定路径的文件。 我们正在使用this.$http进行呼叫。 这是由Vue插件vue-resource提供的,该插件允许我们从Vue实例内部进行RESTful调用。 我们也可以使用jQuery或类似的东西来做同样的事情。 但是,我更喜欢使用vue-resource因为它是为Vue设计的。 为了使它起作用,我们需要告诉Vue使用它。 我们已经在项目中的package.json中将其作为依赖项引入。 让我们告诉Vue。 将以下内容放入您打开调试的src/app.js中。

Vue.use(require('vue-resource'));

In getFiles, we call the get method on this.$http, which will make a GET request to the URL provided and then run the callback supplied and pass the data returned from the GET request. We build the URL by concatenating the base of the Github API with the fullRepoUrl, which we will create in a moment, and then adding the path to the end. For more information on this endpoint, see the Github documentation. In the callback, we are setting this.files to the data returned.

getFiles ,我们在this.$http上调用get方法, this.$http方法将对提供的URL进行GET请求,然后运行提供的回调并传递从GET请求返回的数据。 我们通过将Github API的基础与fullRepoUrl创建的fullRepoUrl连接来构建URL,然后将path添加到末尾。 有关此端点的更多信息,请参见Github文档 。 在回调中,我们将this.files设置为返回的数据。

Let's create the fullRepoUrl property now. Since it isn't part of our data, we can create it as a computed property, meaning Vue will calculate it on the fly whenever we need to use it. Add the following to the configuration object of our component.

让我们现在创建fullRepoUrl属性。 由于它不是我们数据的一部分,因此我们可以将其创建为计算属性,这意味着Vue会在需要使用时立即对其进行计算。 将以下内容添加到组件的配置对象中。

computed: {
        fullRepoUrl: function() {
            return this.username + '/' + this.repo;
        },
    }

This simply creates a computed object with a single method in it. The name of the method becomes the name of the property we access. This method simply concatenates the username and repo together for convenience.

这仅创建一个具有单个方法的计算对象。 方法的名称成为我们访问的属性的名称。 为了方便起见,此方法只是将用户名和存储库连接在一起。

Let's put some of this to use. Let's create our template so we can see something. Create a file at src/js/components/github-file-explorer/template.html and add the following into it.

让我们使用其中的一些。 让我们创建模板,以便我们可以看到一些东西。 在src/js/components/github-file-explorer/template.html创建一个文件,并将以下内容添加到其中。

<div class="row">
        <div class="col-md-12">
            <table class="table">
                <caption>{{ path }}</caption>
                <thead>
                    <tr>
                        <th>Name</th>
                        <th class="text-right"><button class="btn btn-default btn-xs" @click="goBack()" v-if="path !== '/'">Go Back</button></th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="file in files">
                        <td>
                            <div class="file" v-if="file.type === 'file'">
                                <i class="fa fa-file-o"></i>
                                {{ file.name }}
                            </div>
                            <div class="directory" v-if="file.type === 'dir'">
                                <i class="fa fa-folder-o"></i>
                                <a @click="changePath(file.path)">{{ file.name }}</a>
                            </div>
                        </td>
                        <td class="text-right">
                            <a href="{{ file.download_url }}" download v-if="file.type === 'file'">
                                <i class="fa fa-cloud-download"></i>
                            </a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>

Most of this should make sense. We are creating a table with a caption. The caption is the path data property on this component. There is also a back button that fires the goBack method when clicked. We will make this in a minute. Then we iterate through each of the files using v-for and create rows in the table for each one. We use v-if to only show certain icons and links based on the type of the file. Files from Github are generally either files (file) or directories (dir). Then we display a download link but only if it is of type file. Notice here I am using the HTML5 attribute download to tell the browser to download the file and not go to the page pointed to by the href attribute. The availability on this is spotty right now but should be well supported in the future.

其中大多数应该是有道理的。 我们正在创建带有标题的表格。 标题是此组件上的path数据属性。 单击后还有一个后退按钮可触发goBack方法。 我们将在一分钟内完成。 然后,我们使用v-for遍历每个文件v-for并在表中为每个文件创建行。 我们使用v-if仅根据文件类型显示某些图标和链接。 Github中的文件通常是文件( file )或目录( dir )。 然后,我们将显示下载链接,但前提是该链接的类型为file 。 请注意,这里我使用HTML5属性download来告诉浏览器下载文件,而不是转到href属性所指向的页面。 目前的可用性还很差,但将来应该得到很好的支持。

Check out the page in the browser … and there's nothing. Why? Well we have created the component, but we never added it to our index.html. So let's do that! Add the following to the index.html under the hr.

在浏览器中检出页面……什么也没有。 为什么? 好了,我们已经创建了该组件,但是我们从未将其添加到index.html 。 因此,让我们开始吧! 将以下内容添加到hr下的index.html

<github-file-explorer username="laravel" repo="laravel"></github-file-explorer>

This adds the component to our HTML and Vue will display it using the logic we have just created. Notice here we have used kebab-case syntax when we put the component in HTML, but when we defined it in our Vue instance, we used camel-case. This is because JavaScript can't have hyphens in variable names. Don't worry! Vue will make the conversion for us!

这会将组件添加到我们HTML中,Vue将使用我们刚刚创建的逻辑来显示它。 请注意,在将组件放入HTML中时,这里使用了kebab-case语法,但是在Vue实例中定义组件时,我们使用了camel-case。 这是因为JavaScript的变量名中不能包含连字符。 不用担心 Vue将为我们进行转换!

Now, view your application in the browser. Voila! You should be able to see the files that are in the Laravel repository. Let's get changePath and goBack working so we can navigate the whole file structure. Input the following as methods in our component.

现在,在浏览器中查看您的应用程序。 瞧! 您应该能够看到Laravel存储库中的文件。 让changePathgoBack正常工作,以便我们可以导航整个文件结构。 在我们的组件中输入以下内容作为方法。

changePath: function(path) {
        this.path = '/' + path;
        this.getFiles();
    },
    goBack: function() {
        this.path = this.path.split('/').slice(0, -1).join('/');
        if (this.path === '') this.path = '/';

        this.getFiles();
    }

Change path takes in a path, which is passed in from our template, sets the path data property equal to this path with a slash on the front, and then calls getFiles to update the files our Vue instance is watching. goBack removes the right-most portion of the URL and then calls getFiles also.

更改路径采用从模板传递过来的path ,将path数据属性设置为与此路径相同的路径,并在前面加上斜杠,然后调用getFiles来更新Vue实例正在监视的文件。 goBack删除URL的最右边部分,然后也调用getFiles

Check out our application in the browser. Now we can navigate through the file structure! Woohoo! Also give downloading a file a try. Clicking on the link will just download the file to your computer. Super simple.

在浏览器中查看我们的应用程序。 现在我们可以浏览文件结构了! oo! 也可以尝试下载文件。 单击链接只会将文件下载到您的计算机。 超级简单。

Let's do one last thing to change the look of our explorer. Right now the files are spit out in whatever order Github returns them. Let's take back control from Github and reorder them! Let's put the directories on top in alphabetical order and then the files underneath them in alphabetical order. Put the following in the computed object in our component.

让我们做最后一件事来更改浏览器的外观。 现在,文件会以Github返回文件的任何顺序吐出。 让我们从Github收回控制权并重新排序! 让我们按字母顺序将目录放在顶部,然后按字母顺序将目录放在下面。 将以下内容放入我们组件中的computed对象中。

sortedFiles: function() {
        return this.files.slice(0).sort(function(a, b) {
            if (a.type !== b.type) {
                if (a.type === 'dir') {
                    return -1;
                } else {
                    return 1;
                }
            } else {
                if (a.name < b.name) {
                    return -1;
                } else {
                    return 1;
                }
            }
        });
    }

This piece of code gives us a sortedFiles property on our Vue instance that we can use in our template. Since sort changes an array "in place," we create a copy using slice(0). We then call sort on this copy and pass it a custom sorting function that gets passed a pair of our files to compare. This function checks if they are the same type. If they are, then it checks if the type of the first one is a directory. If it is, then it returns -1, which means it should be before the other one in the array. If it's not a directory, then it returns 1, meaning it should be after the other one. If they are the same type, then it will check to see if the name of the first one is less than the name of the second. Easiest way to think about this is uppercase letters are less than lowercase letters and then it goes in order of the alphabet. So "a" is less than "b" and "A" is less than "a."

这段代码为我们提供了Vue实例的sortedFiles属性,可以在模板中使用它。 由于sort可以“就地”更改数组,因此我们使用slice(0)创建了一个副本。 然后,我们对该副本调用sort并将其传递给自定义排序函数,该函数将传递一对文件进行比较。 此功能检查它们是否是相同类型。 如果是,则检查第一个目录的类型是否是目录。 如果是,则返回-1,这意味着它应该在数组中的另一个之前。 如果不是目录,则返回1,表示它应该在另一个目录之后。 如果它们是同一类型,则它将检查第一个的名称是否小于第二个的名称。 考虑这一点的最简单方法是大写字母小于小写字母,然后按字母顺序排列。 因此,“ a”小于“ b”,而“ A”小于“ a”。

Now we need to update our template to use this array instead of the raw files array. Update the v-for to use this array like so.

现在,我们需要更新模板以使用此数组而不是原始files数组。 更新v-for以使用此数组,如下所示。

<tr v-for="file in sortedFiles">

file-explorer

Now refresh the page if it hasn't already and view your work! Boom!

现在,刷新页面(如果尚未显示)并查看您的工作! 繁荣!

使其互动 (Making it Interactive)

So we have a nice Github explorer here, but it isn't all that great if we can't update the repo shown without updating the HTML. Let's make it work with the text field we created earlier. First we need to tell our component to watch the repo prop and update our files when we do. Add the following to our component.

因此,我们在这里有一个不错的Github资源管理器,但是如果不更新HTML就无法更新显示的存储库,那并不是很好。 让我们将其与我们之前创建的文本字段一起使用。 首先,我们需要告诉我们的组件观看回购道具并在执行时更新文件。 将以下内容添加到我们的组件中。

watch: {
        repo: function(newVal, oldVal) {
            this.getFiles();
        }
    },

Here we define a watch object. The keys of this object are properties on the component that should be watched by Vue, and if updated, the callback will be run. In this one, all we do is call our trusty method getFiles to update the files we have locally.

在这里,我们定义了一个watch对象。 该对象的键是Vue应该监视的组件属性,如果更新,则将运行回调。 在这getFiles ,我们要做的就是调用可信任的方法getFiles来更新本地拥有的文件。

Now that we have the component watching the repo prop, we now need to make sure that the prop is dynamic instead of being static as it is now. Change the declaration of the component in our index.html to look like the following.

现在我们已经有了组件来查看repo道具,现在我们需要确保道具是动态的,而不是像现在一样是静态的。 更改index.html组件的声明,如下所示。

<github-file-explorer :username="username" :repo="repo"></github-file-explorer>

Here we are setting up a one-way binding between username on the component and username on the parent. :username references the data attribute username on the component. When we pass in username to it, this means any time the parent's username is updated, then the username property on the component will update also.

在这里,我们在组件上的用户名和父级上的用户名之间建立单向绑定。 :username引用组件上的数据属性username 。 当我们将username传递给它时,这意味着只要更新了父级的用户名,组件上的username属性也会随之更新。

final

Now check it out in the browser. In the text box, type the name of your favorite repo on Github. If you can't think of one try using jakeboyles/MadeInCincy. You will see the files populate. You can now fully navigate the file structure of any public Github repository.

现在,在浏览器中签出。 在文本框中,在Github上键入您喜欢的仓库的名称。 如果您想不出一个尝试使用jakeboyles/MadeInCincy 。 您将看到文件填充。 现在,您可以完全导航任何公共Github存储库的文件结构。

结语 (Wrapping up)

We have done alot. There was alot to talk through. However, think about how little code was actually necessary to write, and the code we did write was not all that crazy. Vue handles all the data-binding stuff for us so that it's one less thing we have to worry about. If you have any questions or just want to say, "Hello," please leave a comment below.

我们做了很多。 有很多话要讲。 但是,考虑一下实际上实际上只需要编写很少的代码,而我们编写的代码并没有那么疯狂。 Vue为我们处理了所有数据绑定事务,因此这是我们不必担心的另一件事。 如果您有任何疑问或只是想说“你好”,请在下面发表评论。

翻译自: https://scotch.io/tutorials/create-a-github-file-explorer-using-vue-js

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值